From 322617e6437dd359341369722ae6bb3848c5c69e Mon Sep 17 00:00:00 2001 From: nikswamy Date: Fri, 22 Dec 2023 14:57:08 -0800 Subject: [PATCH 01/65] Initial commit --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 203 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 00000000000..37dde66d70a --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# PoP-in-FStar +The Proof-oriented Programming in F* Book From 73ee92f269e19583e2ff6b956c7abdeb5517ad1e Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 22 Dec 2023 15:05:21 -0800 Subject: [PATCH 02/65] initial import of files from FStarLang/FStar/doc/book --- book/.gitignore | 3 + book/IncrPair.fst | 38 + book/Makefile | 51 + book/MemCpy.c | 3 + book/MemCpy.fst | 66 + book/_templates/layout.html | 7 + book/_templates/page.html | 14 + book/conf.py | 136 ++ book/fstar_pygments.py | 97 + book/index.rst | 57 + book/intro.rst | 486 ++++ book/notes | 438 ++++ book/part1/part1.rst | 54 + book/part1/part1_equality.rst | 77 + book/part1/part1_execution.rst | 229 ++ book/part1/part1_getting_off_the_ground.rst | 713 ++++++ book/part1/part1_inductives.rst | 378 +++ book/part1/part1_lemmas.rst | 577 +++++ book/part1/part1_polymorphism.rst | 207 ++ book/part1/part1_prop_assertions.rst | 392 ++++ book/part1/part1_quicksort.rst | 410 ++++ book/part1/part1_termination.rst | 431 ++++ book/part1/part1_wrap.rst | 35 + book/part2/part2.rst | 172 ++ book/part2/part2_equality.rst | 453 ++++ book/part2/part2_inductive_type_families.rst | 251 ++ book/part2/part2_logical_connectives.rst | 634 ++++++ book/part2/part2_merkle.rst | 385 ++++ book/part2/part2_par.rst | 686 ++++++ book/part2/part2_phoas.rst | 257 +++ book/part2/part2_stlc.rst | 675 ++++++ book/part2/part2_universes.rst | 759 +++++++ book/part2/part2_vectors.rst | 327 +++ book/part2/part2_well_founded.rst | 243 ++ book/part3/part3.rst | 57 + book/part3/part3_interfaces.rst | 277 +++ book/part3/part3_typeclasses.rst | 685 ++++++ book/part4/part4.rst | 73 + book/part4/part4_background.rst | 93 + .../part4/part4_computation_types_and_tot.rst | 93 + book/part4/part4_div.rst | 547 +++++ book/part4/part4_dm4f.rst.outline | 4 + book/part4/part4_ghost.rst | 526 +++++ book/part4/part4_pure.rst | 794 +++++++ .../part4_user_defined_effects.rst.outline | 53 + book/part5/part5.rst | 46 + book/part5/part5_meta.rst | 523 +++++ book/sec2/Design-of-fstar-Intro.rst.notes | 790 +++++++ book/smt2_pygments.py | 36 + book/static/custom.css | 12 + book/structure.rst | 201 ++ book/under_the_hood/under_the_hood.rst | 17 + book/under_the_hood/uth_smt.rst | 2020 +++++++++++++++++ 53 files changed, 16588 insertions(+) create mode 100644 book/.gitignore create mode 100644 book/IncrPair.fst create mode 100644 book/Makefile create mode 100644 book/MemCpy.c create mode 100644 book/MemCpy.fst create mode 100644 book/_templates/layout.html create mode 100644 book/_templates/page.html create mode 100644 book/conf.py create mode 100644 book/fstar_pygments.py create mode 100644 book/index.rst create mode 100644 book/intro.rst create mode 100644 book/notes create mode 100644 book/part1/part1.rst create mode 100644 book/part1/part1_equality.rst create mode 100644 book/part1/part1_execution.rst create mode 100644 book/part1/part1_getting_off_the_ground.rst create mode 100644 book/part1/part1_inductives.rst create mode 100644 book/part1/part1_lemmas.rst create mode 100644 book/part1/part1_polymorphism.rst create mode 100644 book/part1/part1_prop_assertions.rst create mode 100644 book/part1/part1_quicksort.rst create mode 100644 book/part1/part1_termination.rst create mode 100644 book/part1/part1_wrap.rst create mode 100644 book/part2/part2.rst create mode 100644 book/part2/part2_equality.rst create mode 100644 book/part2/part2_inductive_type_families.rst create mode 100644 book/part2/part2_logical_connectives.rst create mode 100644 book/part2/part2_merkle.rst create mode 100644 book/part2/part2_par.rst create mode 100644 book/part2/part2_phoas.rst create mode 100644 book/part2/part2_stlc.rst create mode 100644 book/part2/part2_universes.rst create mode 100644 book/part2/part2_vectors.rst create mode 100644 book/part2/part2_well_founded.rst create mode 100644 book/part3/part3.rst create mode 100644 book/part3/part3_interfaces.rst create mode 100644 book/part3/part3_typeclasses.rst create mode 100644 book/part4/part4.rst create mode 100644 book/part4/part4_background.rst create mode 100644 book/part4/part4_computation_types_and_tot.rst create mode 100644 book/part4/part4_div.rst create mode 100644 book/part4/part4_dm4f.rst.outline create mode 100644 book/part4/part4_ghost.rst create mode 100644 book/part4/part4_pure.rst create mode 100644 book/part4/part4_user_defined_effects.rst.outline create mode 100644 book/part5/part5.rst create mode 100644 book/part5/part5_meta.rst create mode 100644 book/sec2/Design-of-fstar-Intro.rst.notes create mode 100644 book/smt2_pygments.py create mode 100644 book/static/custom.css create mode 100644 book/structure.rst create mode 100644 book/under_the_hood/under_the_hood.rst create mode 100644 book/under_the_hood/uth_smt.rst diff --git a/book/.gitignore b/book/.gitignore new file mode 100644 index 00000000000..45d59899c3f --- /dev/null +++ b/book/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +code +_build diff --git a/book/IncrPair.fst b/book/IncrPair.fst new file mode 100644 index 00000000000..629175ce07c --- /dev/null +++ b/book/IncrPair.fst @@ -0,0 +1,38 @@ +module IncrPair +open Steel.Memory +open Steel.Effect +open Steel.Reference +open Steel.FractionalPermission +open Steel.Effect.Atomic +open FStar.Ghost +assume +val pts_to (#a:Type u#0) + (r:ref a) + ([@@@ smt_fallback] v:a) + : slprop u#1 + +assume +val read (#a:Type) (#v:Ghost.erased a) (r:ref a) + : Steel a (pts_to r v) (fun x -> pts_to r x) + (requires fun _ -> True) + (ensures fun _ x _ -> x == Ghost.reveal v) + +assume +val write (#a:Type0) (#v:Ghost.erased a) (r:ref a) (x:a) + : SteelT unit (pts_to r v) + (fun _ -> pts_to r x) + +// +let incr (#v:Ghost.erased int) (r:ref int) () + : SteelT unit (pts_to r v) + (fun _ -> pts_to r (v + 1)) + = let x = read r in + write #_ #(Ghost.hide x) r (x + 1); + change_slprop (pts_to r (x + 1)) (pts_to r (v + 1)) (fun _ -> ()) + +//SNIPPET_START: par_incr +let par_incr (#v0 #v1:erased int) (r0 r1:ref int) + : SteelT _ (pts_to r0 v0 `star` pts_to r1 v1) + (fun _ -> pts_to r0 (v0 + 1) `star` pts_to r1 (v1 + 1)) + = par (incr r0) (incr r1) +//SNIPPET_END: par_incr diff --git a/book/Makefile b/book/Makefile new file mode 100644 index 00000000000..856c9853f75 --- /dev/null +++ b/book/Makefile @@ -0,0 +1,51 @@ +# Minimal makefile for Sphinx documentation +# + +export FSTAR_HOME?=$(realpath ../..) + +FSTARLANG_ORG_ROOT?=www + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = FStarBook +SOURCEDIR = . +BUILDDIR = _build + +prep: + cp -R $(FSTAR_HOME)/doc/book/code . + +html: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + find _build -name "*.html" | xargs sed -i 's/_static/static/g' + rm -rf _build/html/static + mv _build/html/_static _build/html/static + +LATEXFILE=proof-orientedprogramminginf.tex + +pdf: + @$(SPHINXBUILD) -M latex "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + sed -i -e 's|\\chapter|\\part|g' $(BUILDDIR)/latex/$(LATEXFILE) + sed -i -e 's|\\section|\\chapter|g' $(BUILDDIR)/latex/$(LATEXFILE) + sed -i -e 's|\\subsection|\\section|g' $(BUILDDIR)/latex/$(LATEXFILE) + sed -i -e 's|\\subsubsection|\\subsection|g' $(BUILDDIR)/latex/$(LATEXFILE) + sed -i -e 's|\\sphinxhref{../code/|\\sphinxhref{https://fstar-lang.org/tutorial/book/code/|g' $(BUILDDIR)/latex/$(LATEXFILE) + sed -i -e 's|\\part{Structure of this book}|\\begin{center}\\bigskip{\\Large \\textbf{Structure of this book}}\\bigskip\\end{center}|g' $(BUILDDIR)/latex/$(LATEXFILE) + $(MAKE) -C $(BUILDDIR)/latex + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +deploy: + rm -rf $(FSTARLANG_ORG_ROOT)/tutorial/book/ + cp -R _build/html $(FSTARLANG_ORG_ROOT)/tutorial/book/ + cp -R code $(FSTARLANG_ORG_ROOT)/tutorial/book/ + cp _build/latex/proof-orientedprogramminginf.pdf $(FSTARLANG_ORG_ROOT)/tutorial/proof-oriented-programming-in-fstar.pdf + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/book/MemCpy.c b/book/MemCpy.c new file mode 100644 index 00000000000..194dab7a576 --- /dev/null +++ b/book/MemCpy.c @@ -0,0 +1,3 @@ +void alloc_copy_free() { + //TODO +} diff --git a/book/MemCpy.fst b/book/MemCpy.fst new file mode 100644 index 00000000000..b32bf92abec --- /dev/null +++ b/book/MemCpy.fst @@ -0,0 +1,66 @@ +(* + Copyright 2008-2018 Microsoft Research + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*) +module MemCpy +open Demo.Deps + +(** + A demo of F*, Low* and KaRaMeL + Copying a buffer of bytes +**) + + +// SNIPPET_START: memcpy +(* + ``memcpy len src cur `` is an imperative procecdure + to copy the contents of the ``src`` array into ``dest`` +*) +let rec memcpy + (len:uint32) //a 32-bit unsigned integero + (cur:uint32{cur <= len}) //current position cur is no more than len + (src dest:larray len uint8) //two arrays whose length is len + : ST unit //A stateful computation, that can read, write, allocate or free memory + (requires fun h -> //with a precondition on the initial state h + live h src /\ //expects src to be live + live h dest /\ //expects dest to be live + disjoint src dest /\ //and the two do not overlap in memory + prefix_equal h src dest cur) //their contents are initially equal up to cur + (ensures fun h0 _ h1 -> //and a postcondition relating their initial and final states + modifies1 dest h0 h1 /\ //modifies only the dest array + prefix_equal h1 src dest len) //and src and dest are equal up to len + = (* The implementation of the function begins here *) + if cur < len + then ( + dest.(cur) <- src.(cur); //copy the cur byte + memcpy len (cur + 1ul) src dest //recurse + ) +// SNIPPET_END: memcpy + + +// SNIPPET_START: alloc_copy_free +let alloc_copy_free + (len:uint32) + (src:lbuffer len uint8) + : ST (lbuffer len uint8) + (requires fun h -> + live h src) + (ensures fun h0 dest h1 -> + live h1 dest /\ + equal h0 src h1 dest) + = let dest = alloc len 0uy in + memcpy len src dest; + free src; + dest +// SNIPPET_END: alloc_copy_free diff --git a/book/_templates/layout.html b/book/_templates/layout.html new file mode 100644 index 00000000000..00b05a6de76 --- /dev/null +++ b/book/_templates/layout.html @@ -0,0 +1,7 @@ +{% extends '!layout.html' %} +{% block document %} +{{super()}} + + + +{% endblock %} diff --git a/book/_templates/page.html b/book/_templates/page.html new file mode 100644 index 00000000000..0707f2a5eb5 --- /dev/null +++ b/book/_templates/page.html @@ -0,0 +1,14 @@ +{% extends "!page.html" %} + +{% block footer %} + +{% endblock %} diff --git a/book/conf.py b/book/conf.py new file mode 100644 index 00000000000..ecd720c3164 --- /dev/null +++ b/book/conf.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/stable/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys + +sys.path.insert(0, os.path.abspath('.')) +import fstar_pygments +import smt2_pygments +from sphinx.highlighting import lexers + +lexers['fstar'] = fstar_pygments.CustomLexer() +lexers['smt2'] = smt2_pygments.CustomLexer() + +def setup(app): + app.add_css_file('custom.css') + +# -- Project information ----------------------------------------------------- + +project = u'Proof-Oriented Programming in F*' +copyright = u'2020, Microsoft Research' +author = u'Nikhil Swamy, Guido Martínez, and Aseem Rastogi' + +# The short X.Y version +version = u'' +# The full version, including alpha/beta/rc tags +release = u'' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.mathjax', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + +html_show_sourcelink = False + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'FStarDoc' + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} diff --git a/book/fstar_pygments.py b/book/fstar_pygments.py new file mode 100644 index 00000000000..d299ac6d143 --- /dev/null +++ b/book/fstar_pygments.py @@ -0,0 +1,97 @@ +from pygments.lexer import RegexLexer, words +from pygments.token import * + +# very rough lexer; not 100% precise +class CustomLexer(RegexLexer): + name = 'FStar' + aliases = ['fstar'] + filenames = ['*.fst', '*.fsti'] + keywords = ( + 'attributes' , + 'noeq' , + 'unopteq' , + 'and' , + 'assert' , + 'assume' , + 'begin' , + 'by' , + 'calc' , + 'class' , + 'decreases' , + 'Dv' , + 'effect' , + 'eliminate' , + 'else' , + 'end' , + 'ensures' , + 'exception' , + 'exists' , + 'false' , + 'friend' , + 'forall' , + 'fun' , + 'function' , + 'GTot' , + 'if' , + 'in' , + 'include' , + 'inline' , + 'inline_for_extraction' , + 'instance' , + 'introduce' , + 'irreducible', + 'let' , + 'logic' , + 'match' , + 'module' , + 'new' , + 'new_effect' , + 'layered_effect' , + 'polymonadic_bind' , + 'polymonadic_subcomp' , + 'SMTPat' , + 'noextract', + 'of' , + 'open' , + 'opaque' , + 'private' , + 'range_of' , + 'rec' , + 'reifiable' , + 'reify' , + 'reflectable', + 'requires' , + 'returns' , + 'set_range_of', + 'sub_effect' , + 'synth' , + 'then' , + 'total' , + 'Tot' , + 'true' , + 'try' , + 'type' , + 'unfold' , + 'unfoldable' , + 'val' , + 'when' , + 'with' , + '_' , + 'Lemma' , + ) + tokens = { + 'root': [ + (r' ', Text), + (r'\n', Text), + (r'\r', Text), + (r'//.*\n', Comment), + (r'\([*]([^*]|[*]+[^)])*[*]+\)', Comment), + (words(keywords, suffix=r'\b'), Keyword), + (r'0x[0-9a-fA-F_]+', Literal.Number), + (r'[0-9_]+', Literal.Number), + (r'[a-zA-Z_]+', Text), + (r'.', Text), + ] + } + +#class CustomFormatter: diff --git a/book/index.rst b/book/index.rst new file mode 100644 index 00000000000..b298e27156e --- /dev/null +++ b/book/index.rst @@ -0,0 +1,57 @@ +.. The main file for the F* manual + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +.. + developed at `Microsoft Research + `_, `MSR-Inria + `_, and `Inria `_. + + +.. role:: smt2(code) + :language: smt2 + +Proof-oriented Programming in F* +================================ + + +F* is a dependently typed programming language and proof +assistant. This book describes how to use F* for *proof-oriented +programming*, a paradigm in which one co-designs programs and proofs +to provide mathematical guarantees about various aspects of a +program's behavior, including properties like functional correctness +(precisely characterizing the input/output behavior of a program), +security properties (e.g., ensuring that a program never leaks certain +secrets), and bounds on resource usage. + +Although a functional programming language at its core, F* promotes +programming in a variety of paradigms, including programming with +pure, total functions, low-level programming in imperative languages +like C and assembly, concurrent programming with shared memory and +message-passing, and distributed programming. Built on top of F*'s +expressive, dependently typed core logic, no matter which paradigm you +choose, proof-oriented programming in F* enables constructing programs +with proofs that they behave as intended. + +**A note on authorship**: Many people have contributed to the +development of F* over the past decade. Many parts of this book too +are based on research papers, libraries, code samples, and language +features co-authored with several other people. However, the +presentation here, including especially any errors or oversights, are +due to the authors. That said, contributions are most welcome and we +hope this book will soon include chapters authored by others. + + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + structure + intro + part1/part1 + part2/part2 + part3/part3 + part4/part4 + part5/part5 + under_the_hood/under_the_hood diff --git a/book/intro.rst b/book/intro.rst new file mode 100644 index 00000000000..087524a7d3a --- /dev/null +++ b/book/intro.rst @@ -0,0 +1,486 @@ +############ +Introduction +############ + +A Capsule Summary of F* +----------------------- + +F* is a dependently type programming language that aims to play +several roles: + +* A general purpose programming language, which encourages + higher-order functional programming with effects, in the tradition + of the ML family of languages. + +* A compiler, which translates F* programs to OCaml or F#, and even C + or Wasm, for execution. + +* A proof assistant, in which to state and prove properties of + programs. + +* A program verification engine, leveraging SMT solvers to partially + automate proofs of programs. + +* A metaprogramming system, supporting the programmatic construction + of F* programs and proof automation procedures. + +To achieve these goals, the design of F* revolves around a few key +elements, described below. Not all of this may make sense to +you---that's okay, you'll learn about it as we go. + +* A core language of total functions with full dependent types, + including an extensional form of type conversion, indexed inductive + types, and pattern matching, recursive functions with semantic + termination checking, dependent refinement types and subtyping, and + polymorphism over a predicative hierarchy of universes. + +* A system of user-defined indexed effects, for modeling, + encapsulating, and statically reasoning about various forms of + computational effects, including a primitive notion of general + recursion and divergence, as well as an open system of user-defined + effects, with examples including state, exceptions, concurrency, + algebraic effects, and several others. + +* A built-in encoding of a classical fragment of F*'s logic into the + first order logic of an SMT solver, allowing many proofs to be + automatically discharged. + +* A reflection within F* of the syntax and proof state of F*, enabling + Meta-F* programs to manipulate F* syntax and proof goals and for + users to build proofs interactively with tactics. + + +DSLs Embedded in F* +~~~~~~~~~~~~~~~~~~~ + +In practice, rather than a single language, the F* ecosystem is also a +collection of domain-specific languages (DSLs). A common use of F* is +to embed within it programming languages at different levels of +abstraction or for specific programming tasks, and for the embedded +language to be engineered with domain-specific reasoning, proof +automation, and compilation backends. Some examples include: + +* Low*, an shallowly embedded DSL for sequential programming against a + C-like memory model including explicit memory management on the + stack and heap; a Hoare logic for partial correctness based on + implicit dynamic frames; and a custom backend (Karamel) to compile + Low* programs to C for further compilation by off-the-shelf C + compilers. + +* EverParse, a shallow embedding of a DSL (layered on top of the Low* + DSL) of parser and serializer combinators, for low-level binary + formats. + +* Vale, a deeply embedded DSL for structured programming in a + user-defined assembly language, with a Hoare logic for total + correctness, and a printer to emit verified programs in a assembly + syntax compatible with various standard assemblers. + +* Steel, a shallow embedding of concurrency as an effect in F*, with + an extensible concurrent separation logic for partial correctness as + a core program logic, and proof automation built using a combination + of Meta-F* tactics, higher-order unification, and SMT. + + +.. _Intro_Vec: + +To get a taste of F*, let's dive right in with some examples. At this +stage, we don't expect you to understand these examples in detail, +though it should give you a flavor of what is possible with F*. + +F* is a dependently typed language +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Dependently typed programming enables one to more precisely capture +properties and invariants of a program using types. Here's a classic +example: the type ``vec a n`` represents an ``n``-dimensional vector +of ``a``-typed elements; or, more simply, a list of ``n`` values each +of type ``a``. Like other dependently typed languages, F* supports +inductively defined definitions of types. + +.. literalinclude:: code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: vec + :end-before: SNIPPET_END: vec + +Operations on a vectors can be given types that describe their +behavior in terms of vector lengths. + +For example, here's a recursive function ``append`` to concatenate two +vectors. Its type shows that the resulting vector has a length that is +the sum of the lengths of the input vectors. + +.. literalinclude:: code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: append + :end-before: SNIPPET_END: append + +Of course, once a function like ``append`` is defined, it can be used +to define other operations and its type helps in proving further +properties. For example, it's easy to show that reversing a vector +does not change its length. + +.. literalinclude:: code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: reverse + :end-before: SNIPPET_END: reverse + +Finally, to get an element from a vector, one can program a selector +whose type also includes a *refinement type* to specify that the index +``i`` is less than the length of the vector. + +.. literalinclude:: code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: get + :end-before: SNIPPET_END: get + +While examples like this can be programmed in other dependently typed +languages, they can often be tedious, due to various technical +restrictions. F* provides a core logic with a more flexible notion of +equality to make programming and proving easier. For now, a takeaway +is that dependently typed programming patterns that are `quite +technical in other languages +`_ are often +fairly natural in F*. You'll learn more about this in :ref:`a later +chapter `. + + +F* supports user-defined effectful programming +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While functional programming is at the heart of the language, F* is +about more than just pure functions. In fact, F* is a Turing complete +language. That this is even worth mentioning may come as a surprise to +readers with a background in general-purpose programming languages +like C# or Scala, but not all dependently typed languages are Turing +complete, since nontermination can break soundness. However, F* +supports general recursive functions and non-termination in a safe +manner, without compromising soundness. + +Beyond nontermination, F* supports a system of user-defined +computational effects which can be used to model a variety of +programming idioms, including things like mutable state, exceptions, +concurrency, IO, etc. + +Here below is some code in an F* dialect called Low* +which provides a sequential, imperative C-like programming model with +mutable memory. The function ``malloc_copy_free`` allocates an array +``dest``, copies the contents of an array of bytes ``src`` into a +``dest``, deallocates ``src`` and returns ``dest``. + +.. literalinclude:: code/MemCpy.fst + :language: fstar + :start-after: SNIPPET_START: malloc_copy_free + :end-before: SNIPPET_END: malloc_copy_free + +It'll take us until much later to explain this code in +full detail, but here are two main points to take away: + + * The type signature of the procedure claims that under specific + constraints on a caller, ``malloc_copy_free`` is *safe* to execute + (e.g., it does not read outside the bounds of allocated memory) + and that it is *correct* (i.e., that it successfully copies + ``src`` to ``dest`` without modifying any other memory) + + * Given the implementation of a procedure, F* actually builds a + mathematical proof that it is safe and correct with respect to its + signature. + +While other program verifiers offer features similar to what we've +used here, a notable thing about F* is that the semantics of programs +with side effects (like reading and writing memory) is entirely +encoded within F*'s logic using a system of user-defined effects. + +Whereas ``malloc_copy_free`` is programmed in Low* and specified using +a particular kind of `Floyd-Hoare logic +`_, there's nothing really +special about it in F*. + +Here, for example, is a concurrent program in another user-defined F* +dialect called Steel. It increments two heap-allocated +references in parallel and is specified for safety and correctness in +`concurrent separation logic +`_, a different kind +of Floyd-Hoare logic than the one we used for ``malloc_copy_free``. + +.. literalinclude:: IncrPair.fst + :language: fstar + :start-after: SNIPPET_START: par_incr + :end-before: SNIPPET_END: par_incr + +As an F* user, you can choose a programming model and a suite of +program proof abstractions to match your needs. You'll learn more +about this in the section on :ref:`user-defined effects `. + +.. _Part1_symbolic_computation: + +F* proofs use SMT solving, symbolic computation and tactics +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Stating a theorem or lemma in F* amounts to declaring a type signature +and a doing a proof corresponds to providing an implementation of that +signature. Proving theorems can take a fair bit of work by a human and +F* seeks to reduce that burden, using a variety of techniques. + +**SMT Solving** + +Proving even a simple program often involves proving dozens or +hundreds of small facts, e.g., proving that bounded arithmetic doesn't +overflow, or that ill-defined operations like divisions by zero never +occur. All these little proofs can quickly overwhelm a user. + +The main workhorse for proofs in F* is an automated theorem prover, +known as a *Satisfiability Modulo Theories*, or SMT, solver. The F* +toolchain integrates the `Z3 SMT Solver +`_. + +By default, the F* typechecker collects all the facts that must be +proven in a program and encodes them to the SMT solver, an engine that +is capable of solving problems in various combinations of mathematical +logics---F* encodes problems to Z3 in a combination of first-order +logic, with uninterpreted functions and integer arithmetic. + +Z3 is remarkably effective at solving the kinds of problems that F* +generates for it. The result is that some F* programs enjoy a high +level of automation, e.g., in ``memcpy``, we specified a pre- and +postcondition and a loop invariant, and the system took care of all +the remaining proofs. + +You'll learn more about how to use leverage Z3 to prove theorems in F* +in :ref:`this chapter `. + +That said, Z3 cannot solve all problems that F* feeds to it. As such, +F* offers several other mechanisms with varying levels of user +control. + + +**Symbolic computation** + +SMT solvers are great at proofs that involve equational rewriting, but +many proofs can be done simply by computation. In fact, proofs by +computation are a distinctive feature of many dependently typed +languages and F* is no exception. + +As a very simple example, consider proving that ``pow2 12 == 4096``, +where ``pow2`` is the recursive function shown below. + +.. literalinclude:: code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: norm_spec + :end-before: SNIPPET_END: norm_spec + +An easy way to convince F* of this fact is to ask it (using +``normalize_term_spec``) to simply compute the result of ``pow2 12`` +on an interpreter that's part of the F* toolchain, which it can do +instantly, rather than relying on an SMT solvers expensive equational +machinery to encode the reduction of a recursive function. + +This reduction machinery (called the *normalizer*) is capable not only +of fully computing terms like ``pow2 12`` to a result, but it can also +partially reduce symbolic F* terms, as shown in the proof below. + +.. literalinclude:: code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: trefl + :end-before: SNIPPET_END: trefl + +The proof invokes the F* normalizer from a tactic called ``T.trefl``, +another F* feature that we'll review quickly, next. + +**Tactics and Metaprogramming** + +Finally, for complete control over a proof, F* includes a powerful +tactic and metaprogramming system. + +Here's a simple example of an interactive proof of a simple fact about +propositions using F* tactics. + +.. literalinclude:: code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: tac + :end-before: SNIPPET_END: tac + +This style of proof is similar to what you might find in systems like +Coq or Lean. An F* tactic is just an F* program that can manipulate F* +proof states. In this case, to prove the theorem +``a ==> b ==> (b /\ a)``, we apply commands to transform the proof +state by applying the rules of propositional logic, building a +proof of the theorem. + +Tactics are an instance of a more general metaprogramming system in +F*, which allows an F* program to generate other F* programs. + + +F* programs compile to OCaml and F#, C and Wasm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Of course, you'll want a way to actually execute the programs you +write. For this, F* provides several ways to compile a program to +other languages for execution, including support to compile programs +to OCaml, F#, C and Wasm. + +As such, a common way to use F* is to develop critical components of +larger software systems in it, use its proof-oriented facilities to +obtain assurances about those components, and then to integrate those +formally proven components into a larger system by compiling the F* +program to C, OCaml, or F# and linking the pieces together. + +In this case, using a tool called `KaRaMeL +`_, a compiler used with F*, we +can produce the following C code for ``memcpy``. + +.. literalinclude:: code/out/MemCpy.c + :language: c + :start-after: SNIPPET_START: malloc_copy_free + :end-before: SNIPPET_END: malloc_copy_free + +Notice that the code we get contains no additional runtime checks: the +detailed requires and ensures clauses are all gone and what's left is +just a plain C code. Later we'll see how to actually write loops, so +that you're not left with recursive functions in C. The point is that +all the proof and specification effort is done before the program is +compiled, imposing no runtime overhead at all. + +To F*, or not to F*? +~~~~~~~~~~~~~~~~~~~~ + +We've quickly seen a bit of what F* has to offer---that may have been +bit overwhelming, if you're new to program proofs. So, you may be +wondering now about whether it's worth learning F* or not. Here are +some things to consider. + +If you like programming and want to get better at it, no matter what +your level is, learning about program proofs will help. Proving a +program, or even just writing down a specification for it, forces you +to think about aspects of your program that you may never have +considered before. There are many excellent resources available to +learn about program proofs, using a variety of other tools, including +some of the following: + + * `Software Foundations + `_: A comprehensive + overview of programming language semantics and formal proofs in + the Coq proof assistant. + + * `A Proof Assistant for Higher-Order Logic + `_: A tutorial on the + Isabelle/HOL proof assistant. + + * `Certified Programming with Dependent Types + `_: Provides an introduction to + proof engineering in Coq. + + * `Type-driven Development + `_: + Introduces using dependent types to developing programs correctly + in Idris. + + * `Theorem Proving in Lean + `_: This is + the standard reference for learning about the Lean theorem prover, + though there are several other `resources + `_ too. + + * `Dafny resources + `_: A different + flavor than all of the above, Dafny is an SMT powered program + verifier for imperative programs. + + * `Liquid Haskell + `_: This + tutorial showcases proving programs with refinement types. + +All of these are excellent resources and each tool has unique +offerings. This book about F* offers a few unique things too. We +discuss a few pros and cons, next. + +**Dependent Types and Extensionality** + +F*'s dependent types are similar in expressiveness to Coq, Lean, Agda, +or Idris, i.e., the expressive power allows formalizing nearly all +kinds of mathematics. What sets F* apart from these other languages +(and more like Nuprl) is its extensional notion of type equality, +making many programming patterns significantly smoother in F* (cf. the +:ref:`vector ` example). However, this design also makes +typechecking in F* undecidable. The practical consequences of this are +that F* typechecker can time-out and refuse to accept your +program. Other dependently typed languages have decidable +typechecking, though they can, in principle, take arbitrarily long to +decide whether or not your program is type correct. + +**A Variety of Proof Automation Tools** + +F*'s use of an SMT solver for proof automation is unique among +languages with dependent types, though in return, one needs to also +trust the combination of F* and Z3 to believe in the validity of an F* +proof. Isabelle/HOL provides similar SMT-assisted automation (in its +Sledgehammer tool), for the weaker logic provided by HOL, though +Sledgehammer's design ensures that the SMT solver need not be +trusted. F*'s use of SMT is also similar to what program verifiers +like Dafny and Liquid Haskell offer. However, unlike their SMT-only +proof strategies, F*, like Coq and Lean, also provides symbolic +reduction, tactics, and metaprogramming. That said, F*'s tactic and +metaprogramming engines are less mature than other systems where +tactics are the primary way of conducting proofs. + +**A Focus on Programming** + +Other dependently typed languages shine in their usage in formalizing +mathematics---Lean's `mathlib +`_ and Coq's +`Mathematical Components `_ are two +great examples. In comparison, to date, relatively little pure +mathematics has been formalized in F*. Rather, F*, with its focus on +effectful programming and compilation to mainstream languages like C, +has been used to it produce industrial-grade high-assurance software, +deployed in settings like the `Windows +`_ +and `Linux `_ kernels, among `many +others `_. + +**Maturity and Community** + +Isabelle/HOL and Coq are mature tools that have been developed and +maintained for many decades, have strong user communities in academia, +and many sources of documentation. Lean's community is growing fast +and also has excellent tools and documentation. F* is less mature, its +design has been the subject of several research papers, making it +somewhat more experimental. The F* community is also smaller, its +documentation is more sparse, and F* users are usually in relatively +close proximity to the F* development team. However, F* developments +also have a good and growing track record of industrial adoption. + + +A Bit of F* History +~~~~~~~~~~~~~~~~~~~ + +F* is an open source project at `GitHub +`_ by researchers at a number of +institutions, including `Microsoft Research +`_, `MSR-Inria +`_, `Inria `_, +`Rosario `_, and `Carnegie-Mellon `_. + +**The name** The F in F* is a homage to System F +(https://en.wikipedia.org/wiki/System_F) which was the base calculus +of an early version of F*. We've moved beyond it for some years now, +however. The F part of the name is also derived from several prior +languages that many authors of F* worked on, including `Fable +`_, `F7 +`_, +`F9 +`_, +`F5 +`_, +`FX +`_, +and even `F# `_. + +The "\*" was meant as a kind of fixpoint operator, and F* was meant to +be a sort of fixpoint of all those languages. The first version of F* +also had affine types and part of the intention then was to use affine +types to encode separation logic---so the "\*" was also meant to evoke +the separation logic "\*". But, the early affine versions of F* never +really did have separation logic. It took until almost +a decade later to have a separation logic embedded in F* (see Steel), +though without relying on affine types. diff --git a/book/notes b/book/notes new file mode 100644 index 00000000000..f8f4e2d05be --- /dev/null +++ b/book/notes @@ -0,0 +1,438 @@ +Comparing F* to other program verifiers +....................................... + +If you're coming to F* having learned about other SMT-backed +verification-oriented languages like `Dafny `_ or `Vcc `_, +you might be wondering if F* is really any different. Here are some +points of similarity and contrast. + +**User-defined language abstractions** + +Perhaps the biggest difference with other program verifiers, is that +rather than offering a fixed set of constructs in which to specify and +verify a progam, F* offers a framework in which users can design their +own abstractions, often at the level of a domain-specific language, in +which to build their programs and proofs. + +More concretely, ``memcpy`` is programmed in a user-defined language +embedded in F* called :ref:`Low* `, which targets sequential, +imperative C-like programming model with mutable heap- and +stack-allocated memory. + +There's nothing particular special about Low*. Here's + + +There are a few differences + + + +The signature of ``memcpy`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The type signature of ``memcpy`` is very detailed, specifying many +properties about the safety and correctness of ``memcpy``, much more +than in most other languages. + +**The arguments of ``memcpy``** + + * ``len``, a 32 bit unsigned integer, or a ``uint32`` + + * ``cur``, also a ``uint32``, representing the current iteration + index, but with a constraint requiring it to be bounded by ``len``. + + * ``src`` and ``dest``, pointers to arrays of bytes (``uint8``), + both with length at least ``len``. + +**The return type and effect** + +The next line ``ST unit`` states that ``memcpy`` is a function, that +may, as a side effect, read, write, allocate or deallocate memory, and +returns a value of type ``unit``---if the ``unit`` type is unfamiliar +to you, from a C or Java programmer's perspective, think of it as +returning ``void``. + +**The precondition** + +Now we get to the really interesting part, the ``requires`` and +``ensures`` clauses, describing the pre- and postconditions of +``memcpy``. In order to safely invoke ``memcpy``, a caller must prove +the following properties when the current state of the program is +``h``: + + * ``live h src``: The ``src`` array has been allocated in memory and + not deallocated yet. This is to ensure that ``memcpy`` does not + attempt to read memory that is not currently allocated, protecting + against common violations of `memory safety + `_, like + `use-after-free bugs + `_. + + * ``live h dest``: Likewise, the ``dest`` array is also allocated + and not deallocated yet. + + * ``disjoint src dest``: The ``src`` and ``dest`` arrays should + point to non-overlapping arrays in memory---if they did not, then + writing to the ``dest`` array could overwrite the contents of the + ``src`` array. + + * ``prefix_equal h src dest cur``: The contents of the ``src`` and + ``dest`` arrays are equal until the current iteration index + ``cur``. + +**The postcondition** + +Finally, the ``ensures`` clause describes what ``memcpy`` guarantees, +by relating the contents of the memory state ``h0`` in when ``memcpy`` +was called to the memory state ``h1`` at the time ``memcpy`` returns. + + * ``modifies1 dest h0 h1`` guarantees that ``memcpy`` only modified + the ``dest`` + + * ``prefix_equal h1 src dest len`` guarantees that the ``src`` and + ``dest`` arrays have equal contents all the way up to ``len`` + +The implementation and proof of ``memcpy`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The signature of ``memcpy`` is significantly longer than its +implementation---that's because many aspects of safety are left +implicit in the implementation and, in this simple case, the +implementation of ``memcpy`` is really quite simple. It just checks if +the ``cur`` index is still less than the length of the array, and, if +so, copies one byte over and recurses while advancing the ``cur`` +position. + +What's left implicit here is a proof that ``memcpy`` actually does +obey its signature. F* actually builds a mathematical proof behind the +scenes that ``memcpy`` is safe and correct with respect to its +specification. In this case, that proof is done by F*'s typechecker, +which makes use of an automated theorem prover called `Z3 +`_ +behind the scenes. As such, if you're willing to trust the +implementations of F* and Z3, you can be confident that ``memcpy`` +does exactly what its specification states, i.e., that the signature +of ``memcpy`` is a *mathematical theorem* about all its executions. + +Compiling ``memcpy`` for execution +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +F* provides several ways to compile a program to other languaegs for +execution, including support to compile programs to OCaml, F#, C and Wasm. + +In this case, using a tool called `KaRaMeL +`_, a compiler used with F*, we +get the following C code for ``memcpy``. + +.. literalinclude:: MemCpy.c + :language: c + +Notice that the code we get contains no additional runtime checks: the +detailed requires and ensures clauses are all gone and what's left is +just a plain C code. Later we'll see how to actually write loops, so +that you're not left with recursive functions in C. The point is that +all the proof and specification effort is done before the program is +compiled, imposing no runtime overhead at all. + + +.. + + It is closely related to several other languages, including other + dependently typed languages like `Coq `_, `Agda + `_, `Idris `_, and `Lean `_, and other SMT-based program + verification engines, like `Dafny `_. + +What makes F* unique is a combination of several elements. + +* Unlike most other dependently typed languages, F* is Turing complete + and has a notion of user-defined effects. It encourages higher-order + functional programming, including general recursion as well as a + user-extensible system of computational effects powerful enough to + express mutable state, exceptions, continuations, algebraic effects + etc. + +* F* is proof assistant, in which to state and prove properties of + programs. Dependently typed proof assistants like + +* A program verification engine, leveraging SMT solvers to partially + automate proofs of programs. + +* A framework within which to embed programming languages, developing + their semantics in a manner suitable for formal proof and enabling + their compilation to a variety of backends, including OCaml, F\#, C, + assembly, Wasm, etc. + +* A metaprogramming system, supporting the programmatic construction + of programs, interactive proofs, and proof automation procedures. + +Many other programming languages are + + +Why not F*? +........... + + + + +To achieve these goals, the design of F* revolves around a few key +elements. + +* A core language of total functions with full dependent types, + including an extensional form of type conversion, indexed inductive + types, and pattern matching, recursive functions with semantic + termination checking, dependent refinement types and subtyping, and + polymorphism over a predicative hierarchy of universes. + +* A system of user-defined indexed effects, for modeling, + encapsulating, and statically reasoning about various forms of + computational effects, including a primitive notion of general + recursion and divergence, as well as an open system of user-defined + effects, with examples including state, exceptions, concurrency, + algebraic effects, and several others. + +* A built-in encoding of a classical fragment of F*'s logic into the + first order logic of an SMT solver, allowing many proofs to be + automatically discharged. + +* A reflection within F* of the syntax and proof state of F*, enabling + Meta-F* programs to manipulate F* syntax and proof goals. + + +Many other programming languages are closely related to F* and address +some of these goals, including other dependently typed languages like +Coq, Agda, Idris and Lean. In comparison with these languages, the +distinctive features of F* include its extensional type conversion and +SMT-based proof automation (both of which make typechecking more +flexible but also undecidable); the use of refinement types (enabling +a concise form of lightweight specification); and its user-defined +effect system. + +This tutorial provides a first taste of +verified programming in F\*. More information about F\*, including +papers and technical reports, can be found on the `F\* website +`_. + +It will help if you are already familiar with functional programming +languages in the ML family (e.g., [OCaml], [F#], [Standard ML]), or +with [Haskell]---we provide a quick review of some basic concepts if +you're a little rusty, but if you feel you need more background, there +are many useful resources freely available on the web, e.g., [Learn +F#], [F# for fun and profit], [Introduction to Caml], the [Real World +OCaml] book, or the [OCaml MOOC]. + +[OCaml]: https://ocaml.org +[F#]: http://fsharp.org/ +[Standard ML]: http://sml-family.org/ +[Haskell]: https://www.haskell.org + +[Learn F#]: https://fsharp.org/learn.html +[F# for fun and profit]: http://fsharpforfunandprofit.com/ +[Introduction to Caml]: https://pl.cs.jhu.edu/pl/lectures/caml-intro.html +[Real World OCaml]: https://realworldocaml.org/ +[OCaml MOOC]: https://www.fun-mooc.fr/courses/course-v1:parisdiderot+56002+session03/about + +~KK +Without any experience in ML or Ocaml my experience has been that the later +exercises are very hard to solve, as some of the notation was not obvious to me. Even +knowing the correct solution, I had to either infer syntax from the exercise +code (which is fine) or go by trial and error (which was frustrating at times). +I will leave comments on the exercises detailing what kind of notation I was (or +am still) missing. Is there a resource (maybe in the wiki) that we can point +readers to, that includes examples for most of the concepts? Something like the +[F# reference] would be really helpful. Also, it might help to specify the +audience of this tutorial a bit more. As a programmer with slightly faded memory +of how inductive proofs work, the lemmas are not very straight forward. As +someone who has never seen ML or has never done any functional programming, the +syntax and some of the patterns are hard to grasp, I feel. +~ + +[F# reference]: https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/ + +The easiest way to try F\* and solve the verification exercises in this tutorial is +directly in your browser by using the [online F\* editor]. You can +load the boilerplate code needed for each exercise into the online +editor by clicking the "Load in editor" link in the body of each +exercise. Your progress on each exercise will be stored in browser +local storage, so you can return to your code later (e.g. if your +browser crashes, etc). + +[online F\* editor]: https://www.fstar-lang.org/run.php + +You can also do this tutorial by installing F\* locally on your +machine. F\* is open source and cross-platform, and you can get +[binary packages] for Windows, Linux, and MacOS X or compile F\* from +the [source code on github] using these [instructions]. + +[binary packages]: https://github.com/FStarLang/FStar/releases +[source code on github]: http://github.com/FStarLang/FStar +[instructions]: https://github.com/FStarLang/FStar/blob/master/INSTALL.md + +You can edit F\* code using your favorite text editor, but for Emacs +the community maintains [fstar-mode.el], a sophisticated extension that adds special +support for F\*, including syntax highlighting, completion, type +hints, navigation, documentation queries, and interactive development +(in the style of CoqIDE or ProofGeneral). +You can find more details about [editor support] on the [F\* wiki]. + +The code for the exercises in this tutorial and their solutions are in the [F\* +repository] on Github. For some exercises, you have to include +additional libraries, as done by the provided Makefiles. +To include libraries for the Emacs interactive mode follow the +[instructions here](https://github.com/FStarLang/fstar-mode.el#including-non-standard-libraries-when-using-fstar-mode). + +~KK +The code available on the tutorial page and on github differs +quite a bit (as does the F\* version I guess). In my case, this lead to some +unexpected errors when copying code from the online-editor to Emacs. It would be +nice to have a pointer to the actual file and maybe the proper parameters to +verify it, in case someone prefers emacs over the online editor. +~ + +[fstar-mode.el]: https://github.com/FStarLang/fstar-mode.el +[Atom]: https://github.com/FStarLang/fstar-interactive +[Vim]: https://github.com/FStarLang/VimFStar +[editor support]: https://github.com/FStarLang/FStar/wiki/Editor-support-for-F* +[F\* wiki]: https://github.com/FStarLang/FStar/wiki +[F\* repository]: https://github.com/FStarLang/FStar/tree/master/doc/tutorial/code/ + +By default F\* only verifies the input code, **it does not execute it**. +To execute F\* code one needs to extract it to OCaml or F# and then +compile it using the OCaml or F# compiler. More details on +[executing F\* code] on the [F\* wiki]. + +[executing F\* code]: https://github.com/FStarLang/FStar/wiki/Executing-F*-code + + + + + + + + +F* is a programming language and proof assistant. + +Part 1: F* Manual + + + +1. F* Quick Start: Online tutorial (chapters 1--6, ported here) + + + +2. The Design of F* + + A Verification-oriented Programming Language and Proof Assistant + (general context from mumon paper) + + * Types + * Dependent refinement types + * Intensional vs extensional, undecidability etc. + * Equality: Definitional and provable equality + * Subtyping + * Proof irrelevance + + * Effects + * Indexed Monads and Effects + * Subsumption and sub-effecting + * Effect abstraction, reification and reflecition + + * Modules and Interfaces + * + + * A Mental Model of the F* Typechecker + * Type inference based on higher order unification + * Normalization and proofs by reflection + * SMT Solving + + * Extraction + * Computational irrelevance and erasure (Ghost) + * Normalization for extraction + * inlining, pure subterms, postprocess(fwd to meta) + +.. _meta-fstar: + + * Scripting F* with Metaprogramming + * Proof states + * Reflecting on syntax + * Quotation + * Scripting extraction + * Hooks + +4. Core libraries + +.. _corelib_Prims: + +Part 2: F* in Action + +.. _TypeConversion: + +1. Foundations of Programming Languages + + a. Simply Typed Lambda Calculus: Syntatic Type Safety + + b. Normalization for STLC + - Hereditary Substitutions: A Syntactic Normalization Technique + - Logical Relations + + c. Semantics of an Imperative Language + + d. Floyd-Hoare Logic + - Hoare triples + - Weakest Preconditions + +2. User-defined Effects + + a. A language with an ML-style heap + + b. Monotonic State + + c. Exceptions + + d. Algebraic Effects + + e. Concurrency + + +.. _LowStar: + +3. Low*: An Embedded DSL and Hoare Logic for Programming + + + - Building on 4a and 4b. + + - Integrating / referencing the Karamel tutorial + +4. A Verified Embedding of a Verified Assembly Language + +5. Modeling and Proving Cryptography Security + + - UF-CMA MAC + + - IND-CPA Encryption + + - Authenticated Encryption + +6. Verified Cryptographic Implementations + +.. _Steel: + +7. Steel: An Extensible Concurrent Separation Logic + +8. EverParse? + * Miniparse + +9. An End-to-end Verified Low-level App + +Part 3: Other resources + + * Recorded coding session + * Talks, presentations + * Exemplary code diff --git a/book/part1/part1.rst b/book/part1/part1.rst new file mode 100644 index 00000000000..64d7340f4e8 --- /dev/null +++ b/book/part1/part1.rst @@ -0,0 +1,54 @@ +.. _Part1: + +############################################ +Programming and Proving with Total Functions +############################################ + + +The core design philosophy of F* is that the type of a term (a program +fragment) is a specification of its runtime behavior. We write ``e : +t`` to mean that a term ``e`` has type ``t``. Many terms can have the +same type and the same term can have many types. + +One (naive but useful) mental model is to think of a type as +describing a set of values. For instance, the type ``int`` describes +the set of terms which compute integer results, i.e., when you have +``e : int``, then when ``e`` is reduced fully it produces a value in +the set ``{..., -2, -1, 0, 1, 2, ...}``. Similarly, the type ``bool`` +is the type of terms that compute or evaluate to one of the values in +the set ``{true,false}``. Unlike many other languages, F* allows +defining types that describe arbitrary sets of values, e.g., the type +that contains only the number ``17``, or the type of functions that +factor a number into its primes. + +When proving a program ``e`` correct, one starts by specifying the +properties one is interested in as a type ``t`` and then trying to +convince F* that ``e`` has type ``t``, i.e., deriving ``e : t``. + +The idea of using a type to specify properties of a program has deep +roots in the connections between logic and computation. You may find +it interesting to read about `propositions as types +`_, +a concept with many deep mathematical and philosophical +implications. For now, it suffices to think of a type ``t`` as a +specification, or a statement of a theorem, and ``e : t`` as +computer-checkable claim that the term ``e`` is a proof of the theorem +``t``. + +In the next few chapters we'll learn about how to program total +functions and prove them correct. + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + part1_getting_off_the_ground + part1_polymorphism + part1_equality + part1_prop_assertions + part1_inductives + part1_termination + part1_lemmas + part1_quicksort + part1_execution + part1_wrap diff --git a/book/part1/part1_equality.rst b/book/part1/part1_equality.rst new file mode 100644 index 00000000000..dd131823a5d --- /dev/null +++ b/book/part1/part1_equality.rst @@ -0,0 +1,77 @@ +.. _Part1_equality: + +Equality +======== + +Equality is a subtle issue that pervades the design of all dependent +type theories, and F* is no exception. In this first chapter, we +briefly touch upon two different kinds of equality in F*, providing +some basic information sufficient for the simplest usages. In a +:ref:`subsequent chapter `, we'll cover equality in +much greater depth. + +Decidable equality and ``eqtype`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We've implicitly used the equality operator ``=`` already (e.g., when +defining ``factorial``). This is the *boolean* equality +operator. Given two terms ``e₁ : t`` and ``e₂ : t``, so long as ``t`` +supports a notion of decidable equality, ``(e₁ = e₂) : bool``. + +To see why not all types support decidably equality, consider ``t`` to +be a function type, like ``int -> int``. To decide if two functions +``f₁, f₂ : int -> int`` are equal, we'd have to apply them to all the +infinitely many integers and compare their results—clearly, this is +not decidable. + +The type ``eqtype`` is the type of types that support decidably +equality. That is, given ``e₁ : t`` and ``e₂ : t``, it is only +permissible to compare ``e₁ = e₂`` if ``t : eqtype``. + +For any type definition, F* automatically computes whether or not that +type is an ``eqtype``. We'll explain later exactly how F* decides +whether or not a type is an ``eqtype``. Roughly, for F* has built-in +knowledge that various primitive types like integers and booleans +support decidable equality. When defining a new type, F* checks +that all values of the new type are composed structurally of terms +that support decidable equality. In particular, if an ``e : t`` may +contain a sub-term that is a function, then ``t`` cannot be an +``eqtype``. + +As such, the type of the decidable equality operator is + +.. code-block:: fstar + + val ( = ) (#a:eqtype) (x:a) (y:a) : bool + +That is, ``x = y`` is well-typed only when ``x : a`` and ``y : a`` and +``a : eqtype``. + +.. note:: + + We see here a bit of F* syntax for defining infix operators. Rather + than only using the ``val`` or ``let`` notation with alphanumeric + identifiers, the notation ``( = )`` introduces an infix operator + defined with non-alphanumeric symbols. You can read more about this + `here + `_. + + + +Propositional equality +^^^^^^^^^^^^^^^^^^^^^^ + +F* offers another notion of equality, propositional equality, written +``==``. For *any type* ``t``, given terms ``e₁, e₂ : t``, the +proposition ``e₁ == e₂`` asserts the (possibly undecidable) equality +of ``e₁`` and ``e₂``. The type of the propositional equality operator +is shown below: + +.. code-block:: fstar + + val ( == ) (#a:Type) (x:a) (y:a) : prop + +Unlike decidable equality ``(=)``, propositional equality is defined +for all types. The result type of ``(==)`` is ``prop``, the type of +propositions. We'll learn more about that in the :ref:`next chapter +`. diff --git a/book/part1/part1_execution.rst b/book/part1/part1_execution.rst new file mode 100644 index 00000000000..07e9b5e745d --- /dev/null +++ b/book/part1/part1_execution.rst @@ -0,0 +1,229 @@ +.. _Part1_execution: + +Executing programs +================== + +We've been through several chapters already, having learned many core +concepts of F*, but we have yet to see how to compile and execute a +program, since our focus so far has been on F*'s logic and how to +prove properties about programs. + +F* offers several choices for executing a program, which we cover +briefly here, using `Quicksort <../code/Part1.Quicksort.Generic.fst>`_ +as a running example. + + +Interpreting F* programs +^^^^^^^^^^^^^^^^^^^^^^^^ + +As mentioned in the :ref:`capsule summary +`, F* includes an engine (called the +*normalizer*) that can symbolically reduce F* computations. We'll see +many more uses of F*'s normalizer as we go, but one basic usage of it +is to use it to interpret programs. + +Invoking the interpreter is easy using :ref:`fstar-mode.el +` in emacs. In emacs, go to the F* menu, then to +*Interactive queries*, then choose *Evaluate an expression* (or type +C-c C-s C-e): this prompts you to enter an expression that you want to +evaluate: type ``sort ( <= ) [4;3;2;1]`` and then press "Enter". You +should see the following output: ``sort ( <= ) [4;3;2;1]`` +:math:`\downarrow\beta\delta\iota\zeta` ``[1; 2; 3; 4] <: Prims.Tot +(list int)``, saying that the input term reduced to ``[1; 2; 3; 4]`` +of type ``Tot (list int)``. + +The :math:`\downarrow\beta\delta\iota\zeta` may seem a bit arcane, but +it describes the reduction strategy that F* used to interpret the term: + + * :math:`\beta` means that functions were applied + * :math:`\delta` means that definitions were unfolded + * :math:`\iota` means that patterns were matched + * :math:`\zeta` means that recursive functions were unrolled + +We'll revisit what these reduction steps mean in a later chapter, +including how to customize it for your needs. + +Compiling to OCaml +^^^^^^^^^^^^^^^^^^ + +The main way to execute F* programs is by compiling, or *extracting*, +them to OCaml and then using OCaml's build system and runtime to +produce an executable and run it. + +.. note:: + + The method that we show here works for simple projects with just a few + files. For larger projects, F* offers a dependence analysis that can + produce dependences for use in a Makefile. F* also offers separate + compilation which allows a project to be checked one file at a time, + and for the results to be cached and reused. For documentation and + examples of how to use these features and structure the build for + larger projects see these resources: + + * `Dealing with dependences `_ + * `Caching verified modules `_ + * `A multifile project `_ + + +Producing an OCaml library +.......................... + +To extract OCaml code from a F* program use the command-line options, +as shown below: + +.. code-block:: + + fstar --codegen OCaml --extract Part1.Quicksort --odir out Part1.Quicksort.Generic.fst + +* The ``--codegen`` option tells F* to produce OCaml code + +* The ``--extract`` option tells F* to only extract all modules in the given namespace, i.e., in this case, all modules in ``Part1.Quicksort`` + +* The ``--odir`` option tells F* to put all the generated files into the specified directory; in this case ``out`` + +* The last argument is the source file to be checked and extracted + +The resulting OCaml code is in the file +``Part1_Quicksort_Generic.ml``, where the F* ``dot``-separated name is +transformed to OCaml's naming convention for modules. The generated code is `here <../code/out/Part1_Quicksort_Generic.ml>`_ + +Some points to note about the extracted code: + +* F* extracts only those definitions that correspond to executable + code. Lemmas and other proof-only aspects are erased. We'll learn + more about erasure in a later chapter. + +* The F* types are translated to OCaml types. Since F* types are more + precise than OCaml types, this translation process necessarily + involves a loss in precision. For example, the type of total orders + in ``Part1.Quicksort.Generic.fst`` is: + + .. code-block:: fstar + + let total_order_t (a:Type) = f:(a -> a -> bool) { total_order f } + + Whereas in OCaml it becomes + + .. code-block:: + + type 'a total_order_t = 'a -> 'a -> Prims.bool + + + This means that you need to be careful when calling your extracted + F* program from unverified OCaml code, since the OCaml compiler will + not complain if you pass in some function that is not a total order + where the F* code expects a total order. + +Compiling an OCaml library +.......................... + +Our extracted code provides several top-level functions (e.g., +``sort``) but not ``main``. So, we can only compile it as a library. + +For simple uses, one can compile the generated code into a OCaml +native code library (a cmxa file) with ``ocamlbuild``, as shown below + +.. code-block:: + + OCAMLPATH=$FSTAR_HOME/lib ocamlbuild -use-ocamlfind -pkg batteries -pkg fstar.lib Part1_Quicksort_Generic.cmxa + +Some points to note: + + * You need to specify the variable ``OCAMLPATH`` which OCaml uses to + find required libraries. For F* projects, the ``OCAMLPATH`` should + include the ``bin`` directory of the FStar release bundle. + + * The ``-use-ocamlfind`` option enables a utility to find OCaml libraries. + + * Extracted F* programs rely on two libraries: ``batteries`` and + ``fstar.lib``, which is what the ``-pkg`` options say. + + * Finally, ``Part1_Quicksort_Generic.cmxa`` references the name of + the corresponding ``.ml`` file, but with the ``.cmxa`` extension + to indicate that we want to compile it as a library. + +Your can use the resulting .cmxa file in your other OCaml projects. + + +Adding a 'main' +............... + +We have focused so far on programming and proving *total* +functions. Total functions have no side effects, e.g., they cannot +read or write state, they cannot print output etc. This makes total +functions suitable for use in libraries, but to write a top-level +driver program that can print some output (i.e., a ``main``), we need +to write functions that actually have some effects. + +We'll learn a lot more about F*'s support for effectful program in a +later section, but for now we'll just provide a glimpse of it by +showing (below) a ```main`` program that calls into our Quicksort +library. + +.. literalinclude:: ../code/Part1.Quicksort.Main.fst + :language: fstar + +There are few things to note here: + + * This time, rather than calling ``Q.sort`` from unverified OCaml + code, we are calling it from F*, which requires us to prove all + its preconditions, e.g., that the comparison function ``( <= )`` + that we are passing in really is a total order---F* does that + automatically. + + * ``FStar.IO.print_string`` is a library function that prints a + string to ``stdout``. Its type is ``string -> ML unit``, a type + that we'll look at in detail when we learn more about effects. + + * The end of the file contains ``let _ = main ()``, a top-level term + that has a side-effect (printing to ``stdout``) when executed. In + a scenario where we have multiple modules, the runtime behavior of + a program with such top-level side-effects depends on the order in + which modules are loaded. When F* detects this, it raises the + warning ``272``. In this case, we intend for this program to have + a top-level effect, so we suppress the warning using the + ``--warn_error -272`` option. + +To compile this code to OCaml, along with its dependence on +``Part1.Quicksort.Generic``, one can invoke: + +.. code-block:: + + fstar --codegen OCaml --extract Part1.Quicksort --odir out Part1.Quicksort.Main.fst + +This time, F* extracts both ``Part1.Quicksort.Generic.fst`` (as +before) and ``Part1.Quicksort.Main.fst`` to OCaml, producing +`Part1_Quicksort_Main.ml <../code/out/Part1_Quicksort_Main.ml>`_ to +OCaml. + +You can compile this code in OCaml to a native executable by doing: + +.. code-block:: + + OCAMLPATH=$FSTAR_HOME/lib ocamlbuild -use-ocamlfind -pkg batteries -pkg fstar.lib Part1_Quicksort_Main.native + +And, finally, you can execute Part1_Quicksort_Main.native to see the +following output: + +.. code-block:: + + $ ./Part1_Quicksort_Main.native + Original list = [42; 17; 256; 94] + Sorted list = [17; 42; 94; 256] + +Compiling to other languages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +F* also supports compiling programs to F# and, for a subset of the +language, supports compilation to C. + +For the F# extraction, use the ``--codegen FSharp`` option. However, +it is more typical to structure an F* project for use with F# using +Visual Studio project and solution files. Here are some examples: + + * `A simple example `_ + + * `A more advanced example mixing F* and F# code `_ + +For extraction to C, please see the `tutorial on Low* `_. + diff --git a/book/part1/part1_getting_off_the_ground.rst b/book/part1/part1_getting_off_the_ground.rst new file mode 100644 index 00000000000..eed91ba4427 --- /dev/null +++ b/book/part1/part1_getting_off_the_ground.rst @@ -0,0 +1,713 @@ +.. _Part1_ch1: + +Getting off the ground +====================== + +To start writing some F* programs, we'll need to learn some basics +about the syntax of the language and some core concepts of types and +functions. + +.. _Part1_editors: + +Text Editors +^^^^^^^^^^^^ + +F* can be used as a command line tool with any text editor. If you're +viewing this in the interactive online tutorial, you can use the +`Ace-based `_ text editor alongside, which +provides some basic conveniences like syntax highlighting. However, +most advanced users of F* use emacs and `fstar-mode.el +`_, which provides several +utilities for interactively editing and checking F* files. We'll refer +to some features specific to fstar-mode.el as we go. + +fstar-mode.el relies on a generic but custom interaction protocol +implemented by the F* compiler. F* also provides a basic +implementation of the `Language Server Protocol +`_, which could +form the basis of integration with other editors. More information +about F*'s editor support can be found `here +`_. + + +Basic syntactic structure +^^^^^^^^^^^^^^^^^^^^^^^^^ + +An F* program is a collection of modules, with each +module represented by a single file with the filename extension +``.fst``. Later, we'll see that a module's interface is in a separate +``.fsti`` file and allows hiding details of a module's implementation +from a client module. + +A module begins with the module's name (which must match the name of +its file, i.e., ``module A`` is in ``A.fst``) and contains a sequence +of top-level signatures and definitions. + +* Signatures ascribe a type to a definition, e.g., ``val f : t``. + +Definitions come in several flavors: the two main forms we'll focus on +when programming with total functions are + +* possibly recursive definitions (let bindings, ``let [rec] f = e``) +* and, inductive type definitions (datatypes, ``type t = | D1 : t1 | ... | Dn : tn``) + +In later sections, we'll see two other kinds of definition: +user-defined indexed effects and sub-effects. + +Comments +^^^^^^^^ + +Block comments are delimited by ``(*`` and ``*)``. Line comments begin +with ``//``. + +.. code-block:: fstar + + (* this is a + block comment *) + + + //This is a line comment + + +Primitives +^^^^^^^^^^ + +Every F* program is checked in the context of some ambient primitive +definitions taken from the core F* module ``Prims``. + +False +..... + +The type ``False`` has no elements. Since there are no terms that +satisfy ``e : False``, the type ``False`` is the type of unprovable +propositions. + +Unit +.... + +The type ``unit`` has a single element denoted ``()``, i.e., ``() : +unit``. + +Booleans +........ + +The type ``bool`` has two elements, ``true`` and ``false``. Note, the +lowercase ``false`` is a boolean constant, distinct from the uppercase +``False`` type. + +The following primitive boolean operators are available, in decreasing +order of precedence. + +* ``not``: Boolean negation (unary, prefix) +* ``&&``: Boolean conjunction (binary, infix) +* ``||``: Boolean disjunction (binary, infix) + +Conditionals +############ + +You can, of course, branch on a boolean with ``if/then/else`` + +.. code-block:: fstar + + if b then 1 else 0 + + if b1 && b2 || b3 + then 17 + else 42 + + +Integers +........ + +The type ``int`` represents unbounded, primitive mathematical +integers. Its elements are formed from the literals ``0, 1, 2, ...``, +and the following primitive operators, in decreasing order of +precedence. + +* ``-``: Unary negation (prefix) +* ``-``: Subtraction (infix) +* ``+``: Addition (infix) +* ``/``: Euclidean division (infix) +* ``%``: Euclidean modulus (infix) +* ``op_Multiply``: Unfortunately, the traditional multiplication + symbol ``*`` is reserved by default for the tuple type + constructor. Use the module ``FStar.Mul`` to treat ``*`` as integer + multiplication. +* ``<`` : Less than (infix) +* ``<=``: Less than or equal (infix) +* ``>`` : Greater than (infix) +* ``>=``: Greater than or equal (infix) + +.. _Part1_ch1_boolean_refinements: + +Boolean refinement types +^^^^^^^^^^^^^^^^^^^^^^^^ + +The F* core library, ``Prims``, defines the type of +natural numbers as follows + +.. code-block:: fstar + + let nat = x:int{x >= 0} + +This is an instance of a boolean refinement type, whose general form +is ``x:t { e }`` where ``t`` is a type, and ``e`` is a ``bool``-typed term +that may refer to the ``t``-typed bound variable ``x``. The term ``e`` +*refines* the type ``t``, in the sense that the set ``S`` denoted by ``t`` +is restricted to those elements ``x`` :math:`\in` ``S`` for which ``e`` evaluates to +``true``. + +That is, the type ``nat`` describes the set of terms that evaluate to an +element of the set ``{0, 1, 2, 3, ...}``. + +But, there's nothing particularly special about ``nat``. You can define +arbitrary refinements of your choosing, e.g., + +.. code-block:: fstar + + let empty = x:int { false } //the empty set + let zero = x:int{ x = 0 } //the type containing one element `0` + let pos = x:int { x > 0 } //the positive numbers + let neg = x:int { x < 0 } //the negative numbers + let even = x:int { x % 2 = 0 } //the even numbers + let odd = x:int { x % 2 = 1 } //the odd numbers + +If you're coming from a language like C or Java where a type primarily +describes some properties about the representation of data in memory, +this view of types as describing arbitrary sets of values may feel a +bit alien. But, let it sink in a bit---types that carve out precise +sets of values will let you state and check invariants about your +programs that may otherwise have only been implicit in your code. + +.. note:: + + Refinement types in F* trace their lineage to `F7 + `_, + a language developed at Microsoft Research c. 2007 -- 2011. `Liquid + Haskell `_ is + another language with refinement types. Those languages provide + additional background and resources for learning about refinement + types. + + Boolean refinements are a special case of a more powerful form of + propositional refinement type in F*. Refinement types, in + conjunction with dependent function types, are, in principle, + sufficient to encode many kinds of logics for program + correctness. However, refinement types are just one among several + tools in F* for program specification and proof. + + +Refinement subtyping +.................... + +We have seen so far how to define a new refinement type, like ``nat`` or +``even``. However, to make use of refinement types we need rules that +allow us to: + +1. check that a program term has a given refinement type, e.g., to + check that ``0`` has type ``nat``. This is sometimes called + *introducing* a refinement type. + +2. make use of a term that has a refinement type, e.g., given ``x : + even`` we would like to be able to write ``x + 1``, treating ``x`` as an + ``int`` to add ``1`` to it. This is sometimes called *eliminating* + a refinement type. + +The technical mechanism in F* that supports both these features is +called *refinement subtyping*. + +If you're used to a language like Java, C# or some other +object-oriented language, you're familiar with the idea of +subtyping. A type ``t`` is a subtype of ``s`` whenever a program term +of type ``t`` can be safely treated as an ``s``. For example, in Java, +all object types are subtypes of the type ``Object``, the base class +of all objects. + +For boolean refinement types, the subtyping rules are as follows: + +* The type ``x:t { p }`` is a subtype of ``t``. That is, given ``e : + (x:t{p})``, it is always safe to *eliminate* the refinement and + consider ``e`` to also have type ``t``. + +* For a term ``e`` of type ``t`` (i.e., ``e : t``), ``t`` is a subtype + of the boolean refinement type ``x:t { p }`` whenever ``p[e / x]`` + (``p[e/x]`` is notation for the term ``p`` with the variable ``x`` + replaced by ``e``), is provably equal to ``true``. In other words, + to *introduce* ``e : t`` at the boolean refinement type ``x:t{ p + }``, it suffices to prove that the term ``p`` with ``e`` substituted + for bound variable ``x``, evaluates to ``true``. + +The elimination rule for refinement types (i.e., the first part above) +is simple---with our intuition of types as sets, the refinement type +``x:t{ p }`` *refines* the set corresponding to ``t`` by the predicate +``p``, i.e., the ``x:t{ p }`` denotes a subset of ``t``, so, of course +``x:t{ p }`` is a subtype of ``t``. + +The other direction is a bit more subtle: ``x:t{ p }`` is only a +subtype of ``p``, for those terms ``e`` that validate ``p``. You're +probably also wondering about how to prove that ``p[e/x]`` evaluates +to ``true``---we will look at this in detail later. But, the short +version is that F*, by default, uses an SMT solver to prove such fact, +though you can also use tactics and other techniques to do so. + +An example +.......... + +Given ``x:even``, consider proving ``x + 1 : odd``; it takes a few +steps: + +1. The operator ``+`` is defined in F*'s library. It expects both its + arguments to have type ``int`` and returns an ``int``. + +2. To prove that the first argument ``x:even`` is a valid argument for + ``+``, we use refinement subtyping to eliminate the refinement and + obtain ``x:int``. The second argument ``1:int`` already has the + required type. Thus, ``x + 1 : int``. + +3. To conclude that ``x + 1 : odd``, we need to introduce a refinement + type, by proving that the refinement predicate of ``odd`` evaluates + to true, i.e., ``x + 1 % 2 = 1``. This is provable by SMT, since we + started with the knowledge that ``x`` is even. + +As such, F* applies subtyping repeatedly to introduce and eliminate +refinement types, applying it multiple times even to check a simple +term like ``x + 1 : odd``. + + +Functions +^^^^^^^^^ + +To start writing interesting programs, we need a way to define +functions. In the core of F*, functions behave like functions in +maths. In other words, they are defined on their entire domain (i.e., +they are total functions and always return a result) and their only +observable behavior is the result they return (i.e., they don't have +any side effect, like looping forever, or printing a message etc.). + +Functions are first-class values in F*, e.g., they can be passed as +arguments to other functions and returned as results. While F* +provides several ways to define functions, the most basic form is the +:math:`\lambda` term, also called a function literal, an anonymous function, or a +simply a *lambda*. The syntax is largely inherited from OCaml, and +this `OCaml tutorial +`_ +provides more details for those unfamiliar with the language. We'll +assume a basic familiarity with OCaml-like syntax. + +Lambda terms +............ + +The term ``fun (x:int) -> x + 1`` defines a function, +a lambda term, which adds 1 to its integer-typed parameter ``x``. You +can also let F* infer the type of the parameter and write ``fun x -> +x + 1`` instead. + +.. _Part1_ch1_named_function: + +Named functions +............... + +Any term in F\* can be given a name using a ``let`` binding. We'll +want this to define a function once and to call it many times. For +example, all of the following are synonyms and bind the lambda term +``fun x -> x + 1`` to the name ``incr`` + +.. code-block:: fstar + + let incr = fun (x:int) -> x + 1 + let incr (x:int) = x + 1 + let incr x = x + 1 + +Functions can take several arguments and the result type of a function +can also be annotated, if desired + +.. code-block:: fstar + + let incr (x:int) : int = x + 1 + let more_than_twice (x:int) (y:int) : bool = x > y + y + +It's considered good practice to annotate all the parameters and +result type of a named function definition. + +.. note:: + + In addition to decorating the types of parameters and the results + of function, F* allows annotating any term ``e`` with its expected + type ``t`` by writing ``e <: t``. This is called a *type + ascription*. An ascription instructs F* to check that the + term ``e`` has the type ``t``. For example, we could have written + +.. code-block:: fstar + + let incr = fun (x:int) -> (x + 1 <: int) + + +Recursive functions +................... + +Recursive functions in F* are always named. To define them, one uses +the ``let rec`` syntax, as shown below. + +.. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: factorial + :end-before: SNIPPET_END: factorial + +This syntax defines a function names ``factorial`` with a single +parameter ``n:nat``, returning a ``nat``. The definition of factorial +is allowed to use the ``factorial`` recursively—as we'll see in a +later chapter, ensuring that the recursion is well-founded (i.e., all +recursive calls terminate) is key to F*'s soundness. However, in this +case, the proof of termination is automatic. + +.. note:: + + Notice the use of `open FStar.Mul` in the example above. This + brings the module `FStar.Mul` into scope and resolves the symbol + ``*`` to integer multiplication. + +F* also supports mutual recursion. We'll see that later. + +.. _Part1_ch1_arrows: + +Arrow types +^^^^^^^^^^^ + +Functions are the main abstraction facility of any functional language +and their types are pervasive in F*. In its most basic form, function +types, or arrows, have the shape:: + + x:t0 -> t1 + +This is the type of a function that + +1. receives an argument ``e`` of type ``t0``, and + +2. always returns a value of type ``t1[e / x]``, i.e., the type of the + returned value depends on the argument ``e``. + +It's worth emphasizing how this differs from function types in other +languages. + +* F*'s arrows are dependent---the type of the result depends on the + argument. For example, we can write a function that returns a + ``bool`` when applied to an even number and returns a ``string`` + when applied to an odd number. Or, more commonly, a function + whose result is one greater than its argument. + +* In F*'s core language, all functions are total, i.e., a function + call always terminates after consuming a finite but unbounded amount + of resources. + +.. note:: + + That said, on any given computer, it is possible for a function + call to fail to return due to resource exhaustion, e.g., running + out of memory. Later, as we look at :ref:`effects `, we + will see that F* also supports writing non-terminating functions. + + +.. _Part1_ch1_arrow_notations: + + +Some examples and common notation +................................. + +1. Functions are *curried*. Functions that take multiple arguments are + written as functions that take the first argument and return a + function that takes the next argument and so on. For instance, the + type of integer addition is:: + + val (+) : x:int -> y:int -> int + +2. Not all functions are dependent and the name of the argument can be + omitted when it is not needed. For example, here's a more concise + way to write the type of ``(+)``:: + + val (+) : int -> int -> int + +3. Function types can be mixed with refinement types. For instance, + here's the type of integer division---the refinement on the divisor + forbids division-by-zero errors:: + + val (/) : int -> (divisor:int { divisor <> 0 }) -> int + +4. Dependence between the arguments and the result type can be used to + state relationships among them. For instance, there are several + types for the function ``let incr = (fun (x:int) -> x + 1)``:: + + val incr : int -> int + val incr : x:int -> y:int{y > x} + val incr : x:int -> y:int{y = x + 1} + + The first type ``int -> int`` is its traditional type in languages + like OCaml. + + The second type ``x:int -> y:int{y > x}`` states that the returned + value ``y`` is greater than the argument ``x``. + + The third type is the most precise: ``x:int -> y:int{y = x + 1}`` + states that the result ``y`` is exactly the increment of the + argument ``x``. + +5. It's often convenient to add refinements on arguments in a + dependent function type. For instance:: + + val f : x:(x:int{ x >= 1 }) -> y:(y:int{ y > x }) -> z:int{ z > x + y } + + Since this style is so common, and it is inconvenient to have to + bind two names for the parameters ``x`` and ``y``, F* allows (and + encourages) you to write:: + + val f : x:int{ x >= 1 } -> y:int{ y > x } -> z:int{ z > x + y } + +6. To emphasize that functions in F*'s core are total functions (i.e., + they always return a result), we sometimes annotate the result type + with the effect label "``Tot``". This label is optional, but + especially as we learn about :ref:`effects `, emphasizing + that some functions have no effects via the ``Tot`` label is + useful. For example, one might sometimes write:: + + val f : x:int{ x >= 1 } -> y:int{ y > x } -> Tot (z:int{ z > x + y }) + + adding a ``Tot`` annotation on the last arrow, to indicate that the + function has no side effects. One could also write:: + + val f : x:int{ x >= 1 } -> Tot (y:int{ y > x } -> Tot (z:int{ z > x + y })) + + adding an annotation on the intermediate arrow, though this is not + customary. + +Exercises +^^^^^^^^^ + +This first example is just to show you how to run the tool and +interpret its output. + +.. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: sample + :end-before: SNIPPET_END: sample + +Notice that the program begins with a ``module`` declaration. It +contains a single definition named ``incr``. Definitions that appear +at the scope of a module are called "top-level" definitions. + +You have several options to try out these examples. + +**F\* online** + +To get started and for trying small exercises, the easiest way is via +the `online tutorial `_. If that's +where you're reading this, you can just use the in-browser editor +alongside which communicates with an F* instance running in the +cloud. Just click `on this link +<../code/exercises/Part1.GettingOffTheGround.fst>`_ to load the +code of an exercise in the editor. + +That said, the online mode can be a bit slow, depending on the load at +the server, and the editor is very minimalistic. + +For anything more than small exercises, you should have a working +local installation of the F* toolchain, as described next. + +**F\* in batch mode** + +You can download pre-built F* binaries `from here +`_. + +Once you have a local installation, to check a program you can run the +``fstar`` at the command line, like so:: + + $ fstar Sample.fst + +In response ``fstar`` should output:: + + Verified module: Sample + All verification conditions discharged successfully + +This means that F* attempted to verify the module named ``Sample``. In +doing so, it generated a some "verification conditions", or proof +obligations, necessary to prove that the module is type correct, and +that is discharged, or proved, all of them successfully. + +**F\* in emacs** + +Rather than running ``fstar`` in batch mode from the command line, F* +programmers using the `emacs `_ +editor often use `fstar-mode.el +`_, an editor plugin that +allows interactively checking an F* program. If you plan to use F* in +any serious way, this is strongly recommended. + +Many types for ``incr`` +....................... + +Here are some types for ``incr``, including some types that are valid +and some others that are not. + +This type claims that ``incr`` result is +greater than its argument and F* agrees—remember, the ``int`` type is +unbounded, so there's no danger of the addition overflowing. + +.. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: ex1.1 + :end-before: SNIPPET_END: ex1.1 + +This type claims that ``incr`` always returns a natural number, but it +isn't true, since incrementing a negative number doesn't always +produce a non-negative number. + +.. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: ex1.2 + :end-before: SNIPPET_END: ex1.2 + +F* produces the following error message:: + + Sample.fst(11,26-11,31): (Error 19) Subtyping check failed; expected type + Prims.nat; got type Prims.int; The SMT solver could not prove the query, try to + spell your proof in more detail or increase fuel/ifuel (see also prims.fst(626, + 18-626,24)) + Verified module: Sample + 1 error was reported (see above) + +**Source location** + +The error message points to ``Sample.fst(11,26-11,31)``, a source +range mentioned the file name, a starting position (line, column), and +an ending position (line, column). In this case, it highlights the +``x + 1`` term. + +**Severity and error code** + +The ``(Error 19)`` mentions a severity (i.e., ``Error``, as opposed +to, say, ``Warning``), and an error code (``19``). + +**Error message** + +The first part of the message stated what you might expect:: + + Subtyping check failed; expected type Prims.nat; got type Prims.int + +The rest of the message provides more details, which we'll ignore for +now, until we've had a chance to explain more about how F* interacts +with the SMT solver. However, one part of the error message is worth +pointing out now:: + + (see also prims.fst(626,18-626,24)) + +Error messages sometimes mention an auxiliary source location in a +"see also" parenthetical. This source location can provide some more +information about why F* rejected a program—in this case, it points to +the constraint ``x>=0`` in the definition of ``nat`` in ``prims.fst``, +i.e., this is the particular constraint that F* was not able to prove. + +So, let's try again. Here's another type for ``incr``, claiming that +if its argument is a natural number then so is its result. This time +F* is happy. + +.. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: ex1.3 + :end-before: SNIPPET_END: ex1.3 + +Sometimes, it is convenient to provide a type signature independently +of a definition. Below, the ``val incr4`` provides only the signature +and the subsequent ``let incr4`` provides the definition—F* checks +that the definition is compatible with the signature. + +.. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: ex1.4 + :end-before: SNIPPET_END: ex1.4 + +Try writing some more types for ``incr``. +(`Load exercise <../code/exercises/Part1.GettingOffTheGround.fst>`_.) + +.. container:: toggle + + .. container:: header + + **Some answers** + + .. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: incr_types + :end-before: SNIPPET_END: incr_types + + +Computing the maximum of two integers +..................................... + +Provide an implementation of the following signature:: + + val max (x:int) (y:int) : int + +There are many possible implementations that satisfy this signature, +including trivial ones like:: + + let max x y = 0 + +Provide an implementation of ``max`` coupled with a type that is +precise enough to rule out definitions that do not correctly return +the maximum of ``x`` and ``y``. + +.. container:: toggle + + .. container:: header + + **Some answers** + + .. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: max + :end-before: SNIPPET_END: max + + +More types for factorial +........................ + +Recall the definition of ``factorial`` from earlier. + +.. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: factorial + :end-before: SNIPPET_END: factorial + +Can you write down some more types for factorial? + +.. container:: toggle + + .. container:: header + + **Some answers** + + .. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: factorial_answers + :end-before: SNIPPET_END: factorial_answers + +Fibonacci +......... + +Here's a doubly recursive function: + +.. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: fibonacci + :end-before: SNIPPET_END: fibonacci + +What other types can you give to it? + +.. container:: toggle + + .. container:: header + + **Some answers** + + .. literalinclude:: ../code/Part1.GettingOffTheGround.fst + :language: fstar + :start-after: SNIPPET_START: fibonacci_answers + :end-before: SNIPPET_END: fibonacci_answers diff --git a/book/part1/part1_inductives.rst b/book/part1/part1_inductives.rst new file mode 100644 index 00000000000..e2b078b02d1 --- /dev/null +++ b/book/part1/part1_inductives.rst @@ -0,0 +1,378 @@ +.. _Part1_ch3: + +Inductive types and pattern matching +==================================== + +In this chapter, you'll learn how to define new types in F*. These +types are called *inductive types*, or, more informally, +datatypes. We'll also learn how to define functions over these +inductive types by pattern matching and to prove properties about +them. + +.. note:: + + We'll only cover the most basic forms of inductive types here. In + particular, the types we show here will not make use of indexing or + any other form of dependent types---we'll leave that for a later + chapter. + +Enumerations +^^^^^^^^^^^^ + +We've seen that ``unit`` is the type with just one element ``()`` and +that ``bool`` is the type with two elements, ``true`` and ``false``. + +You can define your own types with an enumeration of elements, like so. + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: three + :end-before: //SNIPPET_END: three + +This introduces a new type ``three : Type``, and three *distinct* +constants ``One_of_three : three``, ``Two_of_three : three``, +``Three_of_three : three``. These constants are also called +"constructors" or "data constructors". The name of a constructor must +begin with an uppercase letter. + +.. note:: + + In this case, it may seem redundant to have to write the type of + each constructor repeatedly—of course they're all just constructors + of the type ``three``. In this case, F* will allow you to just + write + + .. code-block:: fstar + + type three = + | One_of_three + | Two_of_three + | Three_of_three + + However, in general, as we start to use indexed types, each + constructor can build a different instance of the defined type, so + it will be important to have a way to specify the result type of + each constructor. For uniformity, throughout this book, we'll + always annotate the types of constructors, even when not strictly + necessary. + +F* can prove that they are distinct and that these are the only terms +of type ``three``. + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: assert + :end-before: //SNIPPET_END: assert + +To write functions that case analyze these new types, one uses the +``match`` construct. The syntax of ``match`` in F* is very similar to +OCaml or F#. We'll assume that you're familiar with its basics. As we +go, we'll learn about more advanced ways to use ``match``. + +Here are some examples. + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: disc_handrolled + :end-before: //SNIPPET_END: disc_handrolled + +Discriminators +.............. + +These functions test whether ``x : three`` matches a given +constructor, returning a ``bool`` in each case. Since it's so common +to write functions that test whether a value of an inductive type +matches one of its constructors, F* automatically generates these +functions for you. For example, instead of writing + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: three_as_int + :end-before: //SNIPPET_END: three_as_int + +One can write: + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: three_as_int' + :end-before: //SNIPPET_END: three_as_int' + +In other words, for every constructor ``T`` of an inductive type +``t``, F* generates a function named ``T?`` (called a "discriminator") +which tests if a ``v:t`` matches ``T``. + +Exhaustiveness +.............. + +Of course, an even more direct way of writing ``three_as_int`` is + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: three_as_int'' + :end-before: //SNIPPET_END: three_as_int'' + +Every time you use a ``match``, F* will make sure to prove that you +are handling all possible cases. Try omitting one of the cases in +``three_as_int`` above and see what happens. + +Exhaustiveness checking in F* is a semantic check and can use the SMT +solver to prove that all cases are handled appropriately. For example, +you can write this: + +.. code-block:: fstar + + let only_two_as_int (x:three { not (Three_of_three? x) }) + : int + = match x with + | One_of_three -> 1 + | Two_of_three -> 2 + +The refinement on the argument allows F* to prove that the +``Three_of_three`` case in the pattern is not required, since that +branch would be unreachable anyway. + +.. _Part1_tuples: + +Tuples +^^^^^^ + +The next step from enumerations is to define composite types, e.g., +types that are made from pairs, triples, quadruples, etc. of other +types. Here's how + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: tup + :end-before: //SNIPPET_END: tup + +The type definition for ``tup2 a b`` states that for any types ``a : +Type`` and ``b : Type``, ``Tup2 : a -> b -> tup2 a b``. That is, +``Tup2`` is a constructor of ``tup2``, such that given ``x:a`` and +``y:b``, ``Tup2 x y : tup2 a b``. + +The other types ``tup3`` and ``tup4`` are similar---the type +annotations on the bound variables can be inferred. + +These are inductive types with just one case---so the discriminators +``Tup2?``, ``Tup3?``, and ``Tup4?`` aren't particularly useful. But, +we need a way to extract, or *project*, the components of a tuple. You +can do that with a ``match``. + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: proj_handrolled + :end-before: //SNIPPET_END: proj_handrolled + +Projectors +.......... + +These projectors are common enough that F* auto-generates them for +you. In particular, for any data constructor ``T`` of type +``x1:t1 -> ... -> xn:tn -> t``, F* auto-generates the following function: + + * ``T?.xi : y:t{T? y} -> ti`` + +That is, ``T?.xi`` is a function which when applied to a ``y:t`` in +case ``T? y``, returns the ``xi`` component of ``T x1 ... xn``. + +In the case of our ``tup2`` and ``tup3`` types, we have + + * ``Tup2?.fst``, ``Tup2?.snd`` + * ``Tup3?.fst``, ``Tup3?.snd``, ``Tup3?.thd`` + +Syntax for tuples +................. + +Since tuples are so common, the module ``FStar.Pervasives.Native.fst`` +defines tuple types up to arity 14. So, you shouldn't have to define +``tup2`` and ``tup3`` etc. by yourself. + +The tuple types in ``FStar.Pervasives.Native`` come with syntactic +sugar. + +* You can write ``a & b`` instead of the ``tup2 a b``; ``a & b & c`` + instead of ``tup3 a b c``; and so on, up to arity 14. + +* You can write ``x, y`` instead of ``Tup2 x y``; ``x, y, z`` instead + of ``Tup3 x y z``; an so on, up to arity 14. + +* You can write ``x._1``, ``x._2``, ``x._3``, etc. to project the + field ``i`` of a tuple whose arity is at least ``i``. + +That said, if you're using tuples beyond arity 4 or 5, it's probably a +good idea to define a *record*, as we'll see next—since it can be hard +to remember what the components of a large tuple represent. + +.. _Part1_records: + +Records +....... + +A record is just a tuple with user-chosen names for its fields and +with special syntax for constructing then and projecting their +fields. Here's an example. + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: point + :end-before: //SNIPPET_END: point + +* A record type is defined using curly braces ``{}``. See ``type + point3D`` + +* A record value is also constructed using curly braces, with an + assignment for each field of the record. The fields need not be + given in order. See ``origin``. + +* To access the fields of a record, you can use the dot notation + ``p.x``; See ``dot``, which computes a dot product using dot + notation. + +* Records also support the ``with`` notation to construct a new record + whose fields are the same as the old record, except for those fields + mentioned after the ``with``. That is, ``translate_X p shift`` + returns ``{ x = p.x + shift; y = p.y; z = p.z}``. + +* Records can also be used to pattern match a value. For example, in + ``is_origin``, we match the fields of the record (in any order) + against some patterns. + +Options +^^^^^^^ + +Another common type from F*'s standard library is the ``option`` type, +which is useful to represent a possibly missing value. + +.. code-block::fstar + + type option a = + | None : option a + | Some : a -> option a + +Consider implementing a function to divide ``x / y``, for two integers +``x`` and ``y``. This function cannot be defined when ``y`` is zero, +but it can be defined partially, by excluding the case where ``y = +0``, as shown below. (Of course, one can also refine the domain of the +function to forbid ``y = 0``, but we're just trying to illustrate the +``option`` type here.) + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: option + :end-before: //SNIPPET_END: option + +Like most other functional languages, F* does not have a ``null`` +value. Whenever a value may possibly be ``null``, one typically uses +the ``option`` type, using ``None`` to signify null and ``Some v`` for +the non-null case. + +Unions, or the ``either`` type +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``FStar.Pervasives`` also defines the ``either`` type, shown below. + +.. code-block:: fstar + + type either a b = + | Inl : v: a -> either a b + | Inr : v: b -> either a b + +The type ``either a b`` represents a value that could either be ``Inl +v`` with ``v:a``, or ``Inr v`` with ``v:b``. That is, ``either a b`` +is a tagged union of the ``a`` and ``b``. It's easy to write functions +to analyze the tag ``Inl`` (meaning it's "in the left case") or +``Inr`` ("in the right case") and compute with the underlying +values. Here's an example: + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: either + :end-before: //SNIPPET_END: either + +The ``same_case x y`` function decides if the two unions are both +simultaneously in the left or right case. + +Then, in ``sum x y``, with a refinement that ``x`` and ``y`` are in +the same case, we can handle just two cases (when they are both in +left, or both in right) and F* can prove that the case analysis is +exhaustive. In the left case, the underlying values are boolean, so we +combine them with ``||``; in the right case, the underlying values are +integers, so we combine them with ``+``; and return them with the +appropriate tag. The type of the result ``z:either bool int{ Inl? z <==> +Inl? x}`` shows that the result has the same case as ``x`` (and hence +also ``y``). We could have written the result type as ``z:either bool +int { same_case z x }``. + +.. _Part1_inductives_list: + +Lists +^^^^^ + +All the types we've see far have been inductive only in a degenerate +sense—the constructors do not refer to the types they construct. Now, +for our first truly inductive type, a list. + +Here's the definition of ``list`` from ``Prims``: + +.. code-block:: fstar + + type list a = + | Nil : list a + | Cons : hd:a -> tl:list a -> list a + +The ``list`` type is available implicitly in all F* programs and we +have special (but standard) syntax for the list constructors: + +* ``[]`` is ``Nil`` +* ``[v1; ...; vn]`` is ``Cons v1 ... (Cons vn Nil)`` +* ``hd :: tl`` is ``Cons hd tl``. + +You can always just write out the constructors like `Nil` and `Cons` +explicitly, if you find that useful (e.g., to partially apply ``Cons +hd : list a -> list a``). + +.. _Part1_inductives_length: + +Length of a list +................ + +Let's write some simple functions on lists, starting with computing +the length of a list. + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: length + :end-before: //SNIPPET_END: length + +The ``length`` function is recursive and implicitly polymorphic in a +type ``a``. For any list ``l : list a``, ``length l`` returns a +``nat``. The definition pattern matches on the list and calls +``length`` recursively on the tail of list, until the ``[]`` case is +reached. + +.. _Part1_inductives_append: + +Exercises +^^^^^^^^^ + +`Click here <../code/exercises/Part1.Inductives.fst>`_ for the exercise file. + +Here's the definition of ``append``, a function that concatenates two +lists. Can you give it a type that proves it always returns a list +whose length is the sum of the lengths of its arguments? + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: def append + :end-before: //SNIPPET_END: def append + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: SNIPPET_START: sig append + :end-before: SNIPPET_END: sig append diff --git a/book/part1/part1_lemmas.rst b/book/part1/part1_lemmas.rst new file mode 100644 index 00000000000..2642eddbc76 --- /dev/null +++ b/book/part1/part1_lemmas.rst @@ -0,0 +1,577 @@ +.. _Part1_lemmas: + +Lemmas and proofs by induction +============================== + +Let's say you wrote the ``factorial`` function and gave it the type +``nat -> nat``. Later, you care about some other property about +``factorial``, e.g., that if ``x > 2`` then ``factorial x > x``. One +option is to revise the type you wrote for ``factorial`` and get F\* +to reprove that it has this type. But this isn't always feasible. What +if you also wanted to prove that if ``x > 3`` then ``factorial x > 2 * +x``. Clearly, polluting the type of ``factorial`` with all these +properties that you may or may not care about is impractical. + +You could write assertions to ask F* to check these properties, e.g., + +.. code-block:: fstar + + let _ = assert (forall (x:nat). x > 2 ==> factorial x > 2) + +But, F* complains saying that it couldn't prove this fact. That's not +because the fact isn't true—recall, checking the validity of +assertions in F* is undecidable. So, there are facts that are true +that F* may not be able to prove, at least not without some help. + +In this case, proving this property about ``factorial`` requires a +proof by induction. F* and Z3 cannot do proofs by induction +automatically—you will have to help F* here by writing a *lemma*. + + +Introducing lemmas +^^^^^^^^^^^^^^^^^^ + +A lemma is a function in F* that always returns the ``():unit`` +value. However, the type of lemma carries useful information about +which facts are provable. + +Here's our first lemma: + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: //SNIPPET_START: factorial_is_positive + :end-before: //SNIPPET_END: factorial_is_positive + +There's a lot of information condensed in that definition. Let's spell +it out in detail: + +* ``factorial_is_positive`` is a recursive function with a parameter ``x:nat`` + +* The return type of ``factorial_is_positive`` is a refinement of + unit, namely ``u:unit{factorial x > 0}``. That says that the + function always returns ``()``, but, additionally, when + ``factorial_is_positive x`` returns (which it always does, since it + is a total function) it is safe to conclude that ``factorial x > + 0``. + +* The next three lines prove the lemma using a proof by induction on + ``x``. The basic concept here is that by programming total + functions, we can write proofs about other pure expressions. We'll + discuss such proofs in detail in the remainder of this section. + +.. _Part1_lemma_syntax: + +Some syntactic shorthands for Lemmas +.................................... + +Lemmas are so common in F* that it's convenient to have special syntax +for them. Here's another take at our proof by ``factorial x > 0`` + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: //SNIPPET_START: factorial_is_positive_lemma + :end-before: //SNIPPET_END: factorial_is_positive_lemma + +The type ``x:t -> Lemma (requires pre) (ensures post)`` is the type of +a function + +* that can be called with an argument ``v:t`` +* the argument must satisfies the precondition ``pre[v/x]`` +* the function always returns a ``unit`` +* and ensures that the postcondition ``post[v/x]`` is valid + +The type is equivalent to ``x:t{pre} -> u:unit{post}``. + +When the precondition ``pre`` is trivial, it can be omitted. One can +just write: + +.. code-block:: fstar + + Lemma (ensures post) + +or even + +.. code-block:: fstar + + Lemma post + + +A proof by induction, explained in detail +......................................... + +Let's look at this lemma in detail again—why does it convince F* that +``factorial x > 0``? + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: //SNIPPET_START: factorial_is_positive_lemma + :end-before: //SNIPPET_END: factorial_is_positive_lemma + +* It is a proof by induction on ``x``. Proofs by induction in F* are + represented by total recursive functions. The fact that it is total + is extremely important—it ensures that the inductive argument is + well-founded, i.e., that the induction hypothesis is only applied + correctly on strictly smaller arguments. + +* The base case of the induction is when ``x=0``. In this case, F* + + Z3 can easily prove that ``factorial 0 > 0``, since this just + requires computing ``factorial 0`` to ``1`` and checking ``1 > 0``. + +* What remains is the case where ``x > 0``. + +* In the inductive case, the type of the recursively bound + ``factorial_is_pos`` represents the induction hypothesis. In this + case, its type is + + .. code-block:: fstar + + y:int {y < x} -> Lemma (requires y >= 0) (ensures factorial y > 0) + + In other words, the type of recursive function tells us that for all + ``y`` that are smaller than that current argument ``x`` and + non-negative , it is safe to assume that ``factorial y > 0``. + +* By making a recursive call on ``x-1``, F* can conclude that + ``factorial (x - 1) > 0``. + +* Finally, to prove that ``factorial x > 0``, the solver figures out + that ``factorial x = x * factorial (x - 1)``. From the recursive + lemma invocation, we know that ``factorial (x - 1) > 0``, and since + we're in the case where ``x > 0``, the solver can prove that the + product of two positive numbers must be positive. + +Exercises: Lemmas about integer functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`Click here <../code/exercises/Part1.Lemmas.fst>`_ for the exercise file. + +Exercise 1 +.......... + +Try proving the following lemmas about ``factorial``: + +.. code-block:: fstar + + val factorial_is_greater_than_arg (x:int) + : Lemma (requires x > 2) + (ensures factorial x > x) + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: factorial_is_greater_than_arg + :end-before: SNIPPET_END: factorial_is_greater_than_arg + + +Exercise 2 +.......... + +Try proving the following lemmas about ``fibonacci``: + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: fibonacci_question + :end-before: SNIPPET_END: fibonacci_question + +.. container:: toggle + + .. container:: header + + **Answer** (Includes two proofs and detailed explanations) + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: fibonacci_answer + :end-before: SNIPPET_END: fibonacci_answer + + + Let's have a look at that proof in some detail. It's much like the + proof by induction we discussed in detail earlier, except now we + have two uses of the induction hypothesis. + + * It's a proof by induction on ``n:nat{n >= 2}``, as you can tell from the + ``let rec``. + + * The base cases are when ``n = 2`` and ``n = 3``. In both these + cases, the solver can simply compute ``fibonacci n`` and check + that it is greater than ``n``. + + * Otherwise, in the inductive case, we have ``n >= 4`` and the + induction hypothesis is the type of the recursive function:: + + m:nat{m >= 2 /\ m < n} -> Lemma (fibonacci m >= m) + + * We call the induction hypothesis twice and get:: + + fibonacci (n - 1) >= n - 1 + fibonacci (n - 2) >= n - 2 + + * To conclude, we show:: + + fibonacci n = //by definition + fibonacci (n - 1) + fibonacci (n - 2) >= //from the facts above + (n - 1) + (n - 2) = //rearrange + 2*n - 3 >= //when n >= 4 + n + + As you can see, once you set up the induction, the SMT solver does + a lot of the work. + + Sometimes, the SMT solver can even find proofs that you might not + write yourself. Consider this alternative proof of ``fibonacci n + >= n``. + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: fibonacci_answer_alt + :end-before: SNIPPET_END: fibonacci_answer_alt + + This proof works with just a single use of the induction + hypothesis. How come? Let's look at it in detail. + + 1. It's a proof by induction on ``n:nat{n >= 2}``. + + 2. The base case is when ``n=2``. It's easy to compute ``fibonacci 2`` + and check that it's greater than or equal to 2. + + 3. In the inductive case, we have:: + + n >= 3 + + 4. The induction hypothesis is:: + + m:nat{m >= 2 /\ m < n} -> Lemma (fibonacci m >= m) + + 5. We apply the induction hypothesis to ``n - 1`` and get :: + + fibonacci (n - 1) >= n - 1 + + 6. We have:: + + fibonacci n = //definition + fibonacci (n - 1) + fibonacci (n - 2) >= //from 5 + (n - 1) + fibonacci (n - 2) + + 7. So, our goal is now:: + + (n - 1) + fibonacci (n - 2) >= n + + 8. It suffices if we can show ``fibonacci (n - 2) >= 1`` + + 9. From (2) and the definition of ``fibonacci`` we have:: + + fibonacci (n - 1) = //definition + fibonacci (n - 2) + fibonacci (n - 3) >= //from 5 + n - 1 >= // from 3 + 2 + + + 10. Now, suppose for contradiction, that ``fibonacci (n - 2) = 0``. + + 10.1. Then, from step 9, we have ``fibonacci (n-3) >= 2`` + + 10.2 If ``n=3``, then ``fibonacci 0 = 1``, so we have a contradiction. + + 10.3 If ``n > 3``, then + + 10.3.1. ``fibonacci (n-2) = fibonacci (n-3) + fibonacci (n-4)``, by definition + + 10.3.2. ``fibonacci (n-3) + fibonacci (n-4) >= fibonacci (n-3)``, since ``fibonacci (n-4) : nat``. + + 10.3.3. ``fibonacci (n-2) >= fibonacci (n-3)``, using 10.3.1 and 10.3.2 + + 10.3.4. ``fibonacci (n-2) >= 2``, using 10.1 + + 10.3.5. But, 10.3.4 contradicts 10; so the proof is complete. + + You probably wouldn't have come up with this proof yourself, and + indeed, it took us some puzzling to figure out how the SMT solver + was able to prove this lemma with just one use of the induction + hypothesis. But, there you have it. All of which is to say that + the SMT solver is quite powerful! + +Exercise: A lemma about append +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:ref:`Earlier `, we saw a definition of +``append`` with the following type: + +.. code-block:: fstar + + val append (#a:Type) (l1 l2:list a) + : l:list a{length l = length l1 + length l2} + +Now, suppose we were to define `app``, a version of ``append`` with a +weaker type, as shown below. + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: //SNIPPET_START: def append alt + :end-before: //SNIPPET_END: def append alt + +Can you prove the following lemma? + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: //SNIPPET_START: sig app_length + :end-before: //SNIPPET_END: sig app_length + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: def app_length + :end-before: SNIPPET_END: def app_length + +.. _Part1_intrinsic_extrinsic: + +Intrinsic vs extrinsic proofs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As the previous exercise illustrates, you can prove properties either +by enriching the type of a function or by writing a separate lemma +about it---we call these the 'intrinsic' and 'extrinsic' styles, +respectively. Which style to prefer is a matter of taste and +convenience: generally useful properties are often good candidates for +intrinsic specification (e.g, that ``length`` returns a ``nat``); more +specific properties are better stated and proven as lemmas. However, +in some cases, as in the following example, it may be impossible to +prove a property of a function directly in its type---you must resort +to a lemma. + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: reverse + :end-before: SNIPPET_END: reverse + +Let's try proving that reversing a list twice is the identity +function. It's possible to *specify* this property in the type of +``reverse`` using a refinement type. + +.. code-block:: fstar + + val reverse (#a:Type) : f:(list a -> list a){forall l. l == f (f l)} + +.. note:: + + A subtle point: the refinement on ``reverse`` above uses a + :ref:`propositional equality + `. That's because equality on + lists of arbitrary types is not decidable, e.g., consider ``list + (int -> int)``. All the proofs below will rely on propositional + equality. + +However, F* refuses to accept this as a valid type for ``reverse``: +proving this property requires two separate inductions, neither of +which F* can perform automatically. + +Instead, one can use two lemmas to prove the property we care +about. Here it is: + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: reverse_involutive + :end-before: SNIPPET_END: reverse_involutive + +In the ``hd :: tl`` case of ``rev_involutive`` we are explicitly +applying not just the induction hypothesis but also the ``snoc_cons`` +auxiliary lemma also proven there. + +Exercises: Reverse is injective +............................... + +`Click here <../code/exercises/Part1.Lemmas.fst>`_ for the exercise file. + +Prove that reverse is injective, i.e., prove the following lemma. + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: sig rev_injective + :end-before: SNIPPET_END: sig rev_injective + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: def rev_injective + :end-before: SNIPPET_END: def rev_injective + + That's quite a tedious proof, isn't it. Here's a simpler proof. + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: rev_injective_alt + :end-before: SNIPPET_END: rev_injective_alt + + The ``rev_injective_alt`` proof is based on the idea that every + invertible function is injective. We've already proven that + ``reverse`` is involutive, i.e., it is its own inverse. So, we + invoke our lemma, once for ``l1`` and once for ``l2``. This gives + to the SMT solver the information that ``reverse (reverse l1) = + l1`` and ``reverse (reverse l2) = l2``, which suffices to complete + the proof. As usual, when structuring proofs, lemmas are your + friends! + +Exercise: Optimizing reverse +............................ + +Earlier, we saw how to implement :ref:`a tail-recursive variant +` of ``reverse``. + +.. literalinclude:: ../code/Part1.Termination.fst + :language: fstar + :start-after: SNIPPET_START: rev + :end-before: SNIPPET_END: rev + +Prove the following lemma to show that it is equivalent to the +previous non-tail-recursive implementation, i.e., + +.. code-block:: fstar + + val rev_is_ok (#a:_) (l:list a) : Lemma (rev [] l == reverse l) + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: rev_is_ok + :end-before: SNIPPET_END: rev_is_ok + +Exercise: Optimizing Fibonacci +.............................. + + +Earlier, we saw how to implement :ref:`a tail-recursive variant +` of ``fibonacci``---we show it again below. + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: fib_tail$ + :end-before: SNIPPET_END: fib_tail$ + +Prove the following lemma to show that it is equivalent to the +non-tail-recursive implementation, i.e., + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: val fib_tail_is_ok$ + :end-before: SNIPPET_END: val fib_tail_is_ok$ + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: fib_is_ok$ + :end-before: SNIPPET_END: fib_is_ok$ + +.. _Part1_higher_order_functions: + +Higher-order functions +^^^^^^^^^^^^^^^^^^^^^^^ + +Functions are first-class values—they can be passed to other functions +and returned as results. We've already seen some examples in the +section on :ref:`polymorphism +`. Here are some more, starting with +the ``map`` function on lists. + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: map + :end-before: SNIPPET_END: map + +It takes a function ``f`` and a list ``l`` and it applies ``f`` to +each element in ``l`` producing a new list. More precisely ``map f +[v1; ...; vn]`` produces the list ``[f v1; ...; f vn]``. For example: + +.. code-block:: fstar + + map (fun x -> x + 1) [0; 1; 2] = [1; 2; 3] + + +Exercise: Finding a list element +................................ + +Here's a function called ``find`` that given a boolean function ``f`` +and a list ``l`` returns the first element in ``l`` for which ``f`` +holds. If no element is found ``find`` returns ``None``. + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: find + :end-before: SNIPPET_END: find + +Prove that if ``find`` returns ``Some x`` then ``f x = true``. Is it +better to do this intrinsically or extrinsically? Do it both ways. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: sig find + :end-before: SNIPPET_END: sig find + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: find_alt + :end-before: SNIPPET_END: find_alt + +Exercise: fold_left +................... + +Here is a function ``fold_left``, where:: + + fold_left f [b1; ...; bn] a = f (bn, ... (f b2 (f b1 a))) + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: def fold_left + :end-before: SNIPPET_END: def fold_left + +Prove the following lemma: + +.. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: sig fold_left_Cons_is_rev + :end-before: SNIPPET_END: sig fold_left_Cons_is_rev + +.. container:: toggle + + .. container:: header + + Hint: This proof is a level harder from what we've done so far. + You will need to strengthen the induction hypothesis, and + possibly to prove that ``append`` is associative and that + ``append l [] == l``. + + **Answer** + + .. literalinclude:: ../code/Part1.Lemmas.fst + :language: fstar + :start-after: SNIPPET_START: fold_left_Cons_is_rev + :end-before: SNIPPET_END: fold_left_Cons_is_rev diff --git a/book/part1/part1_polymorphism.rst b/book/part1/part1_polymorphism.rst new file mode 100644 index 00000000000..01d90005596 --- /dev/null +++ b/book/part1/part1_polymorphism.rst @@ -0,0 +1,207 @@ +.. _Part1_polymorphism_and_inference: + +Polymorphism and type inference +=============================== + +In this chapter, we'll learn about defining type polymorphic +functions, or how to work with generic types. + +.. _Part1_type_of_types: + +Type: The type of types +^^^^^^^^^^^^^^^^^^^^^^^ + +One characteristic of F* (and many other dependently typed languages) +is that it treats programs and their types uniformly, all within a +single syntactic class. A type system in this style is sometimes +called a *Pure Type System* or `PTS +`_. + +In F* (as in other PTSs) types have types too, functions can take +types as arguments and return types as results, etc. In particular, +the type of a type is ``Type``, e.g., ``bool : Type``, ``int : Type``, +``int -> int : Type`` etc. In fact, even ``Type`` has a type---as +we'll see when we learn about *universes*. + +Parametric polymorphism or generics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Most modern typed languages provide a way to write programs with +generic types. For instance, C# and Java provide generics, C++ has +templates, and languages like OCaml and Haskell have several kinds of +polymorphic types. + +In F*, writing functions that are generic or polymorphic in types +arises naturally as a special case of the :ref:`arrow types +` that we have already learned about. For example, +here's a polymorphic identity function:: + + let id : a:Type -> a -> a = fun a x -> x + +There are a several things to note here: + +* The type of ``id`` is an arrow type, with two arguments. The first + argument is ``a : Type``; the second argument is a term of type + ``a``; and the result also has the same type ``a``. + +* The definition of ``id`` is a lambda term with two arguments ``a : + Type`` (corresponding to the first argument type) and ``x : a``. The + function returns ``x``---it's an identity function on the second + argument. + +Just as with any function, you can write it instead like this: + +.. literalinclude:: ../code/Part1.Poly.fst + :language: fstar + :start-after: //SNIPPET_START: id + :end-before: //SNIPPET_END: id + +To call ``id``, one can apply it first to a type and then to a value of that type, as shown below. + +.. literalinclude:: ../code/Part1.Poly.fst + :language: fstar + :start-after: //SNIPPET_START: id applications + :end-before: //SNIPPET_END: id applications + +We've defined a function that can be applied to a value ``x:a`` for +any type ``a``. The last line there maybe requires a second read: we +instantiated ``id`` to ``int -> int`` and then applied it to ``id`` +instantiated to ``int``. + +Exercises +^^^^^^^^^ + +Let's try a few simple exercises. `Click here +<../code/exercises/Part1.Poly.fst>`_ for the exercise file. + + +Try defining functions with the following signatures: + +.. literalinclude:: ../code/Part1.Poly.fst + :language: fstar + :start-after: //SNIPPET_START: sig apply_and_compose + :end-before: //SNIPPET_END: sig apply_and_compose + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Poly.fst + :language: fstar + :start-after: //SNIPPET_START: apply_and_compose + :end-before: //SNIPPET_END: apply_and_compose + +How about writing down a signature for ``twice``: + +.. literalinclude:: ../code/Part1.Poly.fst + :language: fstar + :start-after: //SNIPPET_START: def twice + :end-before: //SNIPPET_END: def twice + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Poly.fst + :language: fstar + :start-after: SNIPPET_START: sig twice + :end-before: SNIPPET_END: sig twice + +It's quite tedious to have to explicitly provide that first type +argument to ``id``. Implicit arguments and type inference will help, +as we'll see, next. + + +Type inference: Basics +^^^^^^^^^^^^^^^^^^^^^^ +.. _inference: + +Like many other languages in the tradition of +`Milner's ML `_, +type inference is a central component in F*'s design. + +You may be used to type inference in other languages, where one can +leave out type annotations (e.g., on variables, or when using +type-polymorphic (aka generic) functions) and the compiler determines +an appropriate type based on the surrounding program context. F*'s +type inference includes such a feature, but is considerably more +powerful. Like in other dependently typed language, F*'s inference +engine is based on `higher-order unification +`_ +and can be used to infer arbitrary fragments of program text, not just +type annotations on variables. + +Let's consider our simple example of the definition and use of the +identity function again + +.. literalinclude:: ../code/Part1.Poly.fst + :language: fstar + :start-after: //SNIPPET_START: id + :end-before: //SNIPPET_END: id + +.. literalinclude:: ../code/Part1.Poly.fst + :language: fstar + :start-after: //SNIPPET_START: id applications + :end-before: //SNIPPET_END: id applications + +Instead of explicitly providing that first type argument when applying +``id``, one could write it as follows, replacing the type arguments +with an underscore ``_``. + +.. literalinclude:: ../code/Part1.Poly.fst + :language: fstar + :start-after: //SNIPPET_START: implicit id applications + :end-before: //SNIPPET_END: implicit id applications + +The underscore symbols is a wildcard, or a hole in program, and it's +the job of the F* typechecker to fill in the hole. + +.. note:: + + Program holes are a very powerful concept and form the basis of + Meta-F*, the metaprogramming and tactics framework embedded in + F*---we'll see more about holes in a later section. + +Implicit arguments +^^^^^^^^^^^^^^^^^^ + +Since it's tedious to write an ``_`` everywhere, F* has a notion of +*implicit arguments*. That is, when defining a function, one can add +annotations to indicate that certain arguments can be omitted at call +sites and left for the typechecker to infer automatically. + +For example, one could write + +.. literalinclude:: ../code/Part1.Poly2.fst + :language: fstar + :start-after: //SNIPPET_START: id + :end-before: //SNIPPET_END: id + +decorating the first argument ``a`` with a ``#``, to indicate that it is +an implicit argument. Then at call sites, one can simply write: + +.. literalinclude:: ../code/Part1.Poly2.fst + :language: fstar + :start-after: //SNIPPET_START: id applications + :end-before: //SNIPPET_END: id applications + + +And F* will figure out instantiations for the missing first argument +to ``id``. + +In some cases, it may be useful to actually provide an implicit +argument explicitly, rather than relying on the F* to pick one. For +example, one could write the following: + +.. literalinclude:: ../code/Part1.Poly2.fst + :language: fstar + :start-after: //SNIPPET_START: explicit id applications + :end-before: //SNIPPET_END: explicit id applications + +In each case, we provide the first argument of ``id`` explicitly, by +preceding it with a ``#`` sign, which instructs F* to take the user's +term rather than generating a hole and trying to fill it. diff --git a/book/part1/part1_prop_assertions.rst b/book/part1/part1_prop_assertions.rst new file mode 100644 index 00000000000..7eecbfed302 --- /dev/null +++ b/book/part1/part1_prop_assertions.rst @@ -0,0 +1,392 @@ +.. _Part1_prop_assertions: + +Interfacing with an SMT solver +============================== + +As mentioned :ref:`at the start of this section `, a type ``t`` +represents a proposition and a term ``e : t`` is a proof of ``t``. In +many other dependently typed languages, exhibiting a term ``e : t`` is +the only way to prove that ``t`` is valid. In F*, while one can do +such proofs, it is not the only way to prove a theorem. + +By way of illustration, let's think about :ref:`Boolean refinement +types `. As we've seen already, it is +easy to prove ``17 : x:int{x >= 0}`` in F*. Under the covers, F* +proves that ``(x >= 0) [17/x]`` reduces to ``true``, yet no explicit +term is given to prove this fact. Instead, F* encodes facts about a +program (including things like the semantics of arithmetic operators +like ``>=``) in the classical logic of an SMT solver and asks it (Z3 +typically) to prove whether the formula ``17 >= 0`` is valid in a +context including all encoded facts about a program. If Z3 is able to +prove it valid, F* accepts the formula as true, without ever +constructing a term representing a proof of ``17 >= 0``. + +This design has many important consequences, including, briefly: + +* Trust: F* implicitly trusts its encoding to SMT logic and the + correctness of the Z3 solver. + +* Proof irrelevance: Since no proof term is constructed for proofs + done by SMT, a program cannot distinguish between different proofs + of a fact proven by SMT. + +* Subtyping: Since no proof term is constructed, a term like ``17`` + can have many types, ``int``, ``nat``, ``x:int{x = 17}``, etc. As + mentioned :ref:`earlier `, F* + leverages this to support refinement subtyping. + +* Undecidability: Since Z3 can check the validity of formulas in the + entirety of its logic, including things like quantifying universally + and existentially over infinite ranges, F* does not restrict the the + formulas checked for validity by Z3 to be boolean, or even + decidable. Yes, typechecking in F* is undecidable. + +In this chapter, we'll learn about the the classical logic parts of +F*, i.e., the parts that allow it to interface with an SMT solver. + +.. note:: + + The beginning of this chapter is a little technical, even though + we're not telling the full story behind F*'s classical logic + yet. If parts of it are hard to understand right now, here's what + you need to know to before you :ref:`jump ahead + `. + + F* let's you write quantified formulas, called propositions, like + so + + .. code-block:: fstar + + forall (x1:t1) ... (xn:tn). p + exists (x1:t1) ... (xn:tn). p + + You can build propositions from booleans and conjunctions, + disjunctions, negations, implications, and bi-implications: + + .. code-block:: fstar + + p /\ q //conjunction + p \/ q //disjunction + ~p //negation + p ==> q //implication + p <==> q //bi-implication + + For example, one can say (as shown below) that for all natural + numbers ``x`` and ``y``, if the modulus ``x % y`` is ``0``, then + there exists a natural number ``z`` such that ``x`` is ``z * y``. + + .. code-block:: fstar + + forall (x:nat) (y:nat). x % y = 0 ==> (exists (z:nat). x = z * y) + + F* also has a notion of propositional equality, written ``==``, + that can be used to state that two terms of any type are equal. In + contrast, the boolean equality ``=`` can only be used on types that + support decidable equality. For instance, for ``f1, f2 : int -> + int``, you can write ``f1 == f2`` but you cannot write ``f1 = f2``, + since two functions cannot be decidably compared for equality. + +.. _Part1_prop: + +Propositions +^^^^^^^^^^^^ + +The type ``prop`` defined in ``Prims`` is F*'s type of +proof-irrelevant propositions. More informally, ``prop`` is the type +given to facts that are provable using the SMT solver's classical +logic. + +Propositions defined in ``prop`` need not be decidable. For example, +for a Turing machine ``tm``, the fact ``halts tm`` can be defined as a +``prop``, although it is impossible to decide for an arbitrary ``tm`` +whether ``tm`` halts on all inputs. This is contrast with ``bool``, +the type of booleans ``{true, false}``. Clearly, one could not define +``halts tm`` as a ``bool``, since one would be claiming that for +``halts`` is function that for any ``tm`` can decide (by returning +true or false) whether or not ``tm`` halts on all inputs. + +F* will implicitly convert a ``bool`` to a ``prop`` when needed, since +a decidable fact can be turned into a fact that may be +undecidable. But, when using propositions, one can define things that +cannot be defined in ``bool``, including quantified formulae, as we'll +see next. + +.. _Part1_prop_connectives: + +Propositional connectives +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Consider stating that ``factorial n`` always returns a positive +number, when ``n:nat``. In the :ref:`previous section ` we +learned that one way to do this is to give ``factorial`` a type like so. + +.. code-block:: fstar + + val factorial (n:nat) : x:nat{x > 0} + +Here's another way to state it: + +.. code-block:: fstar + + forall (n:nat). factorial n > 0 + +What about stating that ``factorial n`` can sometimes return a value +that's greater than ``n * n``? + +.. code-block:: fstar + + exists (n:nat). factorial n > n * n + +We've just seen our first use of universal and existential +quantifiers. + +Quantifiers +........... + +A universal quantifier is constructed using the ``forall`` keyword. Its +syntax has the following shape. + +.. code-block:: fstar + + forall (x1:t1) ... (xn:tn) . p + +The ``x1 ... xn`` are bound variables and signify the domain over +which one the proposition ``p`` is quantified. That is, ``forall +(x:t). p`` is valid when for all ``v : t`` the proposition ``p[v/x]`` +is valid. + +And existential quantifier has similar syntax, using the ``exists`` +keyword. + +.. code-block:: fstar + + exists (x1:t1) ... (xn:tn) . p + +In this case, ``exists (x:t). p`` is valid when for some ``v : t`` the +proposition ``p[v/x]`` is valid. + +The scope of a quantifier extends as far to the right as possible. + +As usual in F*, the types on the bound variables can be omitted and F* +will infer them. However, in the case of quantified formulas, it's a +good idea to write down the types, since the meaning of the quantifier +can change significantly depending on the type of the variable. Consider +the two propositions below. + +.. code-block:: fstar + + exists (x:int). x < 0 + exists (x:nat). x < 0 + +The first formula is valid by considering ``x = -1``, while the second +one is not—there is not natural number less than zero. + +It is possible to quantify over any F* type. This makes the +quantifiers higher order and dependent. For example, one can write + +.. code-block:: fstar + + forall (n:nat) (p: (x:nat{x >= n} -> prop)). p n + +.. note:: + + The SMT solver uses a number of heuristics to determine if a + quantified proposition is valid. As you start writing more + substantial F* programs and proofs, it will become important to + learn a bit about these heuristics. We'll cover this in a later + chapter. If you're impatient, you can also read about in on the `F* + wiki + `_. + + +Conjunction, Disjunction, Negation, Implication +............................................... + +In addition to the quantifiers, you can build propositions by +combining them with other propositions, using the operators below, in +decreasing order of precedence. + +**Negation** + +The proposition ``~p`` is valid if the negation of ``p`` is +valid. This is similar to the boolean operator ``not``, but applies to +propositions rather than just booleans. + +**Conjunction** + +The proposition ``p /\ q`` is valid if both ``p`` and ``q`` are +valid. This is similar to the boolean operator ``&&``, but applies to +propositions rather than just booleans. + +**Disjunction** + +The proposition ``p \/ q`` is valid if at least one of ``p`` and ``q`` +are valid. This is similar to the boolean operator ``||``, but applies +to propositions rather than just booleans. + +**Implication** + +The proposition ``p ==> q`` is valid if whenever ``p`` is valid, ``q`` +is also valid. + +**Double Implication** + +The proposition ``p <==> q`` is valid if ``p`` and ``q`` are +equivalent. + +.. note:: + + This may come as a surprise, but these precedence rules mean that + ``p /\ q ==> r`` is parsed as ``(p /\ q) ==> r`` rather than + ``p /\ (q ==> r)``. When in doubt, use parentheses. + + +Atomic propositions +^^^^^^^^^^^^^^^^^^^ + +We've shown you how to form new propositions by building them from +existing propositions using the connectives. But, what about the basic +propositions themselves? + + +Falsehood +......... + +The proposition ``False`` is always invalid. + +Truth +..... + +The proposition ``True`` is always valid. + +.. _Part1_ch2_propositional_equality: + +Propositional equality +...................... + +We learned in the previous chapter about the :ref:`two different forms +of equality `. The type of propositional equality is + +.. code-block:: fstar + + val ( == ) (#a:Type) (x:a) (y:a) : prop + +Unlike decidable equality ``(=)``, propositional equality is defined +for all types. The result type of ``(==)`` is ``prop``, the type of +propositions, meaning that ``x == y`` is a proof-irrelevant +proposition. + + +**Turning a Boolean into a proposition** + +Propositional equality provides a convenient way to turn a boolean +into a proposition. For any boolean ``b``, then term ``b == true`` is +a ``prop``. One seldom needs to do write this manually (although it +does come up occasionally), since F* will automatically insert a +``b==true`` if you're using a ``b:bool`` in a context where a ``prop`` +was expected. + +``Type`` vs. ``prop`` +..................... + +This next bit is quite technical. Don't worry if you didn't understand +it at first. It's enough to know at this stage that, just like +automatically converting a boolean to `prop`, F* automatically +converts any type to ``prop``, when needed. So, you can form new +atomic propositions out of types. + +Every well-typed term in F* has a type. Even types have types, e.g., +the type of ``int`` is ``Type``, i.e., ``int : Type``, ``bool : +Type``, and even ``prop : Type``. We'll have to leave a full +description of this to a later section, but, for now, we'll just +remark that another way to form an atomic proposition is to convert a +type to a proposition. + +For any type ``t : Type``, the type ``_:unit { t } : prop``. We call +this "squashing" a type. This is so common, that F* provides two +mechanisms to support this: + +1. All the propositional connectives, like ``p /\ q`` are designed so + that both ``p`` and ``q`` can be types (i.e., ``p,q : Type``), + rather than propositions, and they implicitly squash their types. + +2. The standard library, ``FStar.Squash``, provides several utilities + for manipulating squashed types. + +.. _Part1_ch2_assertions: + +Assertions +^^^^^^^^^^ + +Now that we have a way to write down propositions, how can we ask F* +to check if those propositions are valid? There are several ways, the +most common of which is an *assertion*. Here's an example: + +.. code-block:: fstar + + let sqr_is_nat (x:int) : unit = assert (x * x >= 0) + +This defines a function ``sqr_is_nat : int -> unit``—meaning it takes +a ``nat`` and always returns ``()``. So, it's not very interesting as +a function. + +However, it's body contains an assertion that ``x * x >= 0``. Now, +many programming languages support runtime assertions—code to check +some property of program when it executes. But, assertions in F* are +different—they are checked by the F* compiler *before* your program is +executed. + +In this case, the ``assert`` instructs F* to encode the program to SMT +and to ask Z3 if ``x * x >= 0`` is valid for an arbitrary integer +``x:int``. If Z3 can confirm this fact (which it can), then F* accepts +the program and no trace of the assertion is left in your program when +it executes. Otherwise the program is rejected at compile time. For +example, if we were to write + +.. code-block:: fstar + + let sqr_is_pos (x:int) : unit = assert (x * x > 0) + +Then, F* complains with the following message:: + + Ch2.fst(5,39-5,50): (Error 19) assertion failed; The SMT solver could not prove the query, try to spell your proof in more detail or increase fuel/ifuel + +You can use an assertion with any proposition, as shown below. + +.. literalinclude:: ../code/Part1.Assertions.fst + :language: fstar + :start-after: //SNIPPET_START: max + :end-before: //SNIPPET_END: max + +Assumptions +^^^^^^^^^^^ + +The dual of an assertion is an assumption. Rather than asking F* and +Z3 to prove a fact, an assumption allows one to tell F* and Z3 to +accept that some proposition is valid. You should use assumptions with +care—it's easy to make a mistake and assume a fact that isn't actually +true. + +The syntax of an assumption is similar to an assertion. Here, below, +we write ``assume (x <> 0)`` to tell F* to assume ``x`` is non-zero in +the rest of the function. That allows F* to prove that the assertion +that follows is valid. + +.. code-block:: fstar + + let sqr_is_pos (x:int) = assume (x <> 0); assert (x * x > 0) + +Of course, the assertion is not valid for all ``x``—it's only valid +for those ``x`` that also validate the preceding assumption. + +Just like an ``assert``, the type of ``assume p`` is ``unit``. + +There's a more powerful form of assumption, called an ``admit``. The +term ``admit()`` can given any type you like. For example, + +.. code-block:: fstar + + let sqr_is_pos (x:int) : y:nat{y > 0} = admit() + +Both ``assume`` and ``admit`` can be helpful when you're working +through a proof, but a proof isn't done until it's free of them. diff --git a/book/part1/part1_quicksort.rst b/book/part1/part1_quicksort.rst new file mode 100644 index 00000000000..a8b59454e4c --- /dev/null +++ b/book/part1/part1_quicksort.rst @@ -0,0 +1,410 @@ +.. _Part1_quicksort: + +Case Study: Quicksort +===================== + +We'll now put together what we've learned about defining recursive +functions and proving lemmas about them to prove the correctness of +`Quicksort `_, a classic +sorting algorithm. + +We'll start with lists of integers and describe some properties that +we'd like to hold true of a sorting algorithm, starting with a +function ``sorted``, which decides when a list of integers is sorted +in increasing order, and ``mem``, which decides if a given element is +in a list. Notice that ``mem`` uses an ``eqtype``, :ref:`the type of +types that support decidable equality `. + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: sorted mem + :end-before: SNIPPET_END: sorted mem + +Given a sorting algorithm ``sort``, we would like to prove the +following property, meaning that for all input list ``l``, the +resulting list ``sort l`` is sorted and has all the elements that +``l`` does. + +.. code-block:: fstar + + forall l. sorted (sort l) /\ (forall i. mem i l <==> mem i (sort l)) + +This specification is intentionally a bit weak, e.g., in case there +are multiple identical elements in ``l``, this specification does not +prevent ``sort`` from retaining only one of them. + +We will see how to improve this specification below, as part of an +exercise. + +If you're unfamiliar with the algorithm, you can `read more about it +here `_. We'll describe +several slightly different implementations and proofs of Quicksort in +detail—you may find it useful to follow along interactively with the +`entire code development <../code/Part1.Quicksort.fst>`_ of this +sequence. + +Implementing ``sort`` +^^^^^^^^^^^^^^^^^^^^^ + +Our implementation of Quicksort is pretty simple-minded. It always +picks the first element of the list as the pivot; partitions the rest +of the list into those elements greater than or equal to the pivot, +and the rest; recursively sorts the partitions; and slots the pivot in +the middle before returning. Here it is: + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: sort-impl + :end-before: SNIPPET_END: sort-impl + +There are a few points worth discussing in detail: + +1. The notation ``((<=) pivot)`` may require some explanation: it is + the *partial application* of the ``<=`` operator to just one + argument, ``pivot``. It is equivalent to ``fun x -> pivot <= x``. + +2. We have to prove that ``sort`` terminates. The measure we've + provided is ``length l``, meaning that at each recursive call, + we're claiming that the length of input list is strictly + decreasing. + +3. Why is this true? Well, informally, the recursive calls ``sort lo`` + and ``sort hi`` are partitions of the ``tl`` of the list, which is + strictly shorter than ``l``, since we've removed the ``pivot`` + element. We'll have to convince F* of this fact by giving + ``partition`` an interesting type that we'll see below. + +Implementing ``partition`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Here's an implementation of ``partition``. It's a :ref:`higher-order +function `, where ``partition f l`` +returns a pair of lists ``l₁`` and ``l₂``, a partitioning of the +elements in ``l`` such that the every element in ``l₁`` satisfies +``f`` and the elements in ``l₂`` do not. + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: partition + :end-before: SNIPPET_END: partition + +The specification we've given ``partition`` is only partial—we do not +say, for instance, that all the elements in ``l₁`` satisfy ``f``. We +only say that the sum of the lengths of the ``l₁`` and ``l₂`` are +equal to the length of ``l``. That's because that's the only property +we need (so far) about ``partition``—this property about the lengths +is what we need to prove that on the recursive calls ``sort lo`` and +``sort hi``, the arguments ``lo`` and ``hi`` are strictly shorter than +the input list. + +This style of partial specification should give you a sense of the art +of program proof and the design choices between :ref:`intrinsic and +extrinsic proof `. One tends to specify +only what one needs, rather than specifying all properties one can +imagine right up front. + +Proving ``sort`` correct +^^^^^^^^^^^^^^^^^^^^^^^^ + +Now that we have our definition of ``sort``, we still have to prove it +correct. Here's a proof—it requires three auxiliary lemmas and we'll +discuss it in detail. + +Our first lemma relates ``partition`` to ``mem``: it proves what we +left out in the intrinsic specification of ``partition``, i.e., that +all the elements in ``l₁`` satisfy ``f``, the elements in ``l₂`` do +not, and every element in ``l`` appears in either ``l₁`` or ``l₂``. + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: partition_mem + :end-before: SNIPPET_END: partition_mem + +Our next lemma is very specific to Quicksort. If ``l₁`` and ``l₂`` are +already sorted, and partitioned by ``pivot``, then slotting ``pivot`` +in the middle of ``l₁`` and ``l₂`` produces a sorted list. The +specification of ``sorted_concat`` uses a mixture of refinement types +(e.g., ``l1:list int{sorted l1}``) and ``requires`` / ``ensures`` +specifications–this is just a matter of taste. + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: sorted_concat + :end-before: SNIPPET_END: sorted_concat + +Our third lemma is a simple property about ``append`` and ``mem``. + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: append_mem + :end-before: SNIPPET_END: append_mem + +Finally, we can put the pieces together for our top-level statement +about the correctness of ``sort``. + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: sort_correct + :end-before: SNIPPET_END: sort_correct + +The structure of the lemma is mirrors the structure of ``sort`` +itself. + +* In the base case, the proof is automatic. + +* In the inductive case, we partition the tail of the list and + recursively call the lemma on the the ``hi`` and ``lo`` components, + just like ``sort`` itself. The intrinsic type of ``partition`` is + also helpful here, using the ``length`` measure on the list to prove + that the induction here is well-founded. + + - To prove the ``ensures`` postcondition, we apply our three + auxiliary lemmas. + + + ``partition_mem ((<=) pivot) tl`` gives us the precondition of + needed to satisfy the ``requires`` clause of + ``sorted_concat``. + + + We also need to prove the ``sorted`` refinements on ``sort lo`` + and ``sort hi`` in order to call ``sorted_concat``, but the + recursive calls of the lemma give us those properties. + + + After calling ``sorted_concat``, we have proven that the + resulting list is sorted. What's left is to prove that all the + elements of the input list are in the result, and ``append_mem`` + does that, using the postcondition of ``partition_mem`` and the + induction hypothesis to relate the elements of ``append (sort + lo) (pivot :: sort hi)`` to the input list ``l``. + +Here's another version of the ``sort_correct`` lemma, this time +annotated with lots of intermediate assertions. + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: sort_correct_annotated + :end-before: SNIPPET_END: sort_correct_annotated + +This is an extreme example, annotating with assertions at almost every +step of the proof. However, it is indicative of a style that one often +uses to interact with F* when doing SMT-assisted proofs. At each point +in your program or proof, you can use ``assert`` to check what the +prover "knows" at that point. See what happens if you move the +assertions around, e.g., if you move ``assert (sort_ok lo)`` before +calling ``sort_correct_annotated lo``, F* will complain that it is not +provable. + +Limitations of SMT-based proofs at higher order +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You may be wondering why we used ``(<=) pivot`` instead of ``fun x -> +pivot <= x`` in our code. Arguably, the latter is more readable, +particularly to those not already familiar with functional programming +languages. Well, the answer is quite technical. + +We could indeed have written ``sort`` like this, + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: sort_alt + :end-before: SNIPPET_END: sort_alt + +And we could have tried to write our main lemma this way: + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: sort_alt_correct + :end-before: SNIPPET_END: sort_alt_correct + +However, without further assistance, F*+SMT is unable to prove the +line at which the ``assume`` appears. It turns out, this is due to a +fundamental limitation in how F* encodes its higher-order logic into +the SMT solver's first-order logic. This encoding comes with some loss +in precision, particularly for lambda terms. In this case, the SMT +solver is unable to prove that the occurrence of ``fun x -> pivot <= +x`` that appears in the proof of ``sort_alt_correct_annotated`` is +identical to the occurrence of the same lambda term in ``sort_alt``, +and so it cannot conclude that ``sort_alt l`` is really equal to +``append (sort_alt lo) (pivot :: sort_alt hi))``. + +This is unfortunate and can lead to some nasty surprises when trying +to do proofs about higher order terms. Here are some ways to avoid +such pitfalls: + +* Try to use named functions at higher order, rather than lambda + literals. Named functions do not suffer a loss in precision when + encoded to SMT. This is the reason why ``(<=) pivot`` worked out + better than the lambda term here—the ``(<=)`` is a name that + syntactically appears in both the definition of ``sort`` and the + proof of ``sort_alt_correct`` and the SMT solver can easily see that + the two occurrences are identical. + +* If you must use lambda terms, sometimes an intrinsic proof style can + help, as we'll see below. + +* If you must use lambda terms with extrinsic proofs, you can still + complete your proof, but you will have to help F* along with tactics + or proofs by normalization, more advanced topics that we'll cover in + later sections. + +* Even more forward looking, recent `higher-order variants of SMT + solvers `_ are promising and + may help address some of these limitations. + +An intrinsic proof of ``sort`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As we observed earlier, our proof of ``sort_correct`` had essentially +the same structure as the definition of ``sort`` itself—it's tempting +to fuse the definition of ``sort`` with ``sort_correct``, so that we +avoid the duplication and get a proof of correctness of ``sort`` +built-in to its definition. + +So, here it is, a more compact proof of ``sort``, this time done +intrinsically, i.e., by enriching the type of ``sort`` to capture the +properties we want. + +.. literalinclude:: ../code/Part1.Quicksort.fst + :language: fstar + :start-after: SNIPPET_START: sort_intrinsic + :end-before: SNIPPET_END: sort_intrinsic + +We still use the same three auxiliary lemmas to prove the properties +we want, but this time the recursive calls to sort the partitioned +sub-lists also serve as calls to the induction hypothesis for the +correctness property we're after. + +Notice also that in this style, the use of a lambda literal isn't +problematic—when operating within the same scope, F*'s encoding to SMT +is sufficiently smart to treat the multiple occurrences of ``fun x -> +pivot <= x`` as identical functions. + +Runtime cost? +............. + +You may be concerned that we have just polluted the definition of +``sort_intrinsic`` with calls to three additional recursive +functions–will this introduce any runtime overhead when executing +``sort_intrinsic``? Thankfully, the answer to that is "no". + +As we'll learn in the section on :ref:`effects `, F* supports +of notion of *erasure*—terms that can be proven to not contribute to +the observable behavior of a computation will be erased by the +compiler before execution. In this case, the three lemma invocations +are total functions returning unit, i.e., these are functions that +always return in a finite amount of time with the constant value +``()``, with no other observable side effect. So, there is no point in +keeping those function calls around—we may as well just optimize them +away to their result ``()``. + +Indeed, if you ask F* to extract the program to OCaml (using +``fstar --codegen OCaml``), here's what you get: + +.. code-block:: fstar + + let rec (sort_intrinsic : Prims.int Prims.list -> Prims.int Prims.list) = + fun l -> + match l with + | [] -> [] + | pivot::tl -> + let uu___ = partition (fun x -> pivot <= x) tl in + (match uu___ with + | (hi, lo) -> + append (sort_intrinsic lo) (pivot :: (sort_intrinsic hi))) + +The calls to the lemmas have disappeared. + +Exercises +^^^^^^^^^ + +Generic sorting +............... + +Here's `a file with the scaffolding for this exercise +<../code/exercises/Part1.Quicksort.Generic.fst>`_. + +The point of this exercise is to define a generic version of ``sort`` +that is parameterized by any total order over the list elements, +rather than specializing ``sort`` to work on integer lists only. Of +course, we want to prove our implementations correct. So, let's do it +in two ways, both intrinsically and extrinsically. Your goal is to +remove the all the occurrences of ``admit`` in the development below. + +.. literalinclude:: ../code/exercises/Part1.Quicksort.Generic.fst + :language: fstar + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Quicksort.Generic.fst + :language: fstar + + +Proving that ``sort`` is a permutation +...................................... + +We promised at the beginning of this section that we'd eventually give +a better specification for ``sort``, one that proves that it doesn't +drop duplicate elements in the list. That's the goal of the exercise +in this section—we'll prove that our generic Quicksort is returns a +permutation of the input list. + +Let's start by defining what it means for lists to be permutations of +each other—we'll do this using occurrence counts. + +.. literalinclude:: ../code/exercises/Part1.Quicksort.Permutation.fst + :language: fstar + :start-after: //SNIPPET_START: count permutation + :end-before: //SNIPPET_END: count permutation + +The definitions should be self-explanatory. We include one key lemma +``append_count`` to relate occurrence to list concatenations. + +The next key lemma to prove is ``partition_mem_permutation``. + +.. code-block:: fstar + + val partition_mem_permutation (#a:eqtype) + (f:(a -> bool)) + (l:list a) + : Lemma (let l1, l2 = partition f l in + (forall x. mem x l1 ==> f x) /\ + (forall x. mem x l2 ==> not (f x)) /\ + (is_permutation l (append l1 l2))) + +You will also need a lemma similar to the following: + +.. code-block:: fstar + + val permutation_app_lemma (#a:eqtype) (hd:a) (tl l1 l2:list a) + : Lemma (requires (is_permutation tl (append l1 l2))) + (ensures (is_permutation (hd::tl) (append l1 (hd::l2)))) + +Using these, and adaptations of our previous lemmas, prove: + +.. code-block:: fstar + + val sort_correct (#a:eqtype) (f:total_order_t a) (l:list a) + : Lemma (ensures + sorted f (sort f l) /\ + is_permutation l (sort f l)) + +Load the `exercise script +<../code/exercises/Part1.Quicksort.Permutation.fst>`_ and give it a +try. + + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Quicksort.Permutation.fst + :language: fstar + + + + diff --git a/book/part1/part1_termination.rst b/book/part1/part1_termination.rst new file mode 100644 index 00000000000..373b4aaaf27 --- /dev/null +++ b/book/part1/part1_termination.rst @@ -0,0 +1,431 @@ +.. _Part1_termination: + +Proofs of termination +===================== + +It's absolutely crucial to the soundness of F*'s core logic that all +functions terminate. Otherwise, one could write non-terminating +functions like this:: + + let rec loop (x:unit) : False = loop x + +and show that ``loop () : False``, i.e., we'd have a proof term for +``False`` and the logic would collapse. + +In the previous chapter, we just saw how to define recursive functions +to :ref:`compute the length of list ` and to +:ref:`append two lists `. We also said +:ref:`earlier ` that all functions in F*'s core are +*total*, i.e., they always return in a finite amount of time. So, you +may be wondering, what is it that guarantees that recursive function +like ``length`` and ``append`` actually terminate on all inputs? + +The full details of how F* ensures termination of all functions in its +core involves several elements, including positivity restrictions on +datatype definitions and universe constraints. However, the main thing +that you'll need to understand at this stage is that F* includes a +termination check that applies to the recursive definitions of total +functions. The check is a semantic check, not a syntactic criterion, +like in some other dependently typed languages. + +We quickly sketch the basic structure of the F\* termination check on +recursive functions---you'll need to understand a bit of this in order +to write more interesting programs. + +A well-founded partial order on terms +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to prove a function terminating in F\* one provides a +*measure*: a pure expression depending on the function's +arguments. F\* checks that this measure strictly decreases on each +recursive call. The measure for the arguments of the call is compared +to the measure for the previous call according to a well-founded +partial order on F\* terms. We write `v1 << v2` when `v1` precedes +`v2` in this order. + +.. note:: + + A relation `R` is a well-founded partial order on a set `S` if, and + only if, `R` is a partial order on `S` and there are no infinite + descending chains in `S` related by `R`. For example, taking `S` to + be `nat`, the set of natural numbers, the integer ordering `<` is a + well-founded partial order (in fact, it is a total order). + +Since the measure strictly decreases on each recursive call, and there +are no infinite descending chains, this guarantees that the function +eventually stops making recursive calls, i.e., it terminates. + +.. _Part1_precedes_relation: + +The precedes relation +..................... + +Given two terms ``v1:t1`` and ``v2:t2``, we can prove ``v1 << v2`` +if any of the following are true: + +1. **The ordering on integers**: + + ``t1 = nat`` and ``t2 = nat`` and ``v1 < v2`` + + Negative integers are not related by the `<<` relation, which is + only a _partial_ order. + +2. **The sub-term ordering on inductive types** + + If ``v2 = D u1 ... un``, where ``D`` is a constructor of an + inductive type fully applied to arguments ``u1`` to ``un``, then + ``v1 << v2`` if either + + * ``v1 = ui`` for some ``i``, i.e., ``v1`` is a sub-term of ``v2`` + + * ``v1 = ui x`` for some ``i`` and ``x``, i.e., ``v1`` is the + result of applying a sub-term of ``v2`` to some argument ``x``. + + +.. _Part1_why_length_terminates: + + +Why ``length`` terminates +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Let's look again at the definition of ``length`` and see how F* checks +that it terminates, i.e., + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: length + :end-before: //SNIPPET_END: length + +First off, the definition of ``length`` above makes use of various +syntactic shorthands to hide some details. If we were to write it out +fully, it would be as shown below: + +.. code-block:: fstar + + let rec length #a (l:list a) + : Tot nat (decreases l) + = match l with + | [] -> 0 + | _ :: tl -> length tl + +The main difference is on the second line. As opposed to just writing +the result type of ``length``, in full detail, we write +``Tot nat (decreases l)``. This states two things + +* The ``Tot nat`` part states that ``length`` is a total function + returning a ``nat``, just as the ``nat`` did before. + +* The additional ``(decreases l)`` specifying a *measure*, i.e., the + quantity that decreases at each recursive call according the + well-founded relation ``<<``. + +To check the definition, F* gives the recursively bound name +(``length`` in this case) a type that's guarded by the measure. I.e., +for the body of the function, ``length`` has the following type: + +.. code-block:: fstar + + #a:Type -> m:list a{ m << l } -> nat + +This is to say that when using ``length`` to make a recursive call, we +can only apply it to an argument ``m << l``, i.e., the recursive call +can only be made on an argument ``m`` that precedes the current +argument ``l``. This is enough to ensure that the recursive calls will +eventually bottom out, since there are no infinite descending chains +related by ``<<``. + +In the case of ``length``, we need to prove at the recursive call +``length tl`` that ``tl : (m : list a { m << l })``, or, equivalently +that ``tl << l`` is valid. But, from the sub-term ordering on +inductive types, ``l = Cons _ tl``, so ``tl << l`` is indeed provable +and everything checks out. + +.. _Part1_lexicographic_orderings: + +Lexicographic orderings +^^^^^^^^^^^^^^^^^^^^^^^ + +F* also provides a convenience to enhance the well-founded ordering +``<<`` to lexicographic combinations of ``<<``. That is, given two +lists of terms ``v1, ..., vn`` and ``u1, ..., un``, F* accepts that +the following lexicographic ordering:: + + v1 << u1 ‌‌\/ (v1 == u1 /\ (v2 << u2 ‌‌\/ (v2 == u2 /\ ( ... vn << un)))) + +is also well-founded. In fact, it is possible to prove in F* that this +ordering is well-founded, provided ``<<`` is itself well-founded. + +Lexicographic ordering are common enough that F* provides special +support to make it convenient to use them. In particular, the +notation:: + + %[v1; v2; ...; vn] << %[u1; u2; ...; un] + +is shorthand for:: + + v1 << u1 ‌‌\/ (v1 == u1 /\ (v2 << u2 ‌‌\/ (v2 == u2 /\ ( ... vn << un)))) + +Let's have a look at lexicographic orderings at work in proving that +the classic ``ackermann`` function terminates on all inputs. + +.. literalinclude:: ../code/Part1.Termination.fst + :language: fstar + :start-after: SNIPPET_START: ackermann + :end-before: SNIPPET_END: ackermann + +The ``decreases %[m;n]`` syntax tells F* to use the lexicographic +ordering on the pair of arguments ``m, n`` as the measure to prove +this function terminating. + +When defining ``ackermann m n``, for each recursive call of the form +``ackermann m' n'``, F* checks that ``%[m';n'] << %[m;n]``, i.e., F* +checks that either + +* ``m' << m``, or +* ``m' = m`` and ``n' << n`` + +There are three recursive calls to consider: + +1. ``ackermann (m - 1) 1``: In this case, since we know that ``m > + 0``, we have ``m - 1 << m``, due to the ordering on natural + numbers. Since the ordering is lexicographic, the second argument + is irrelevant for termination. + +2. ``ackermann m (n - 1)``: In this case, the first argument remained + the same (i.e., it's still ``m``), but we know that ``n > 0`` so + ``n - 1 << n`` by the natural number ordering. + +3. ``ackermann (m - 1) (ackermann m (n - 1))``: Again, like in the + first case, the first argument ``m - 1 << m``, and the second is + irrelevant for termination. + +.. _Part1_termination_default_measures: + +Default measures +^^^^^^^^^^^^^^^^ + +As we saw earlier, F* allows you to write the following code, with no +``decreases`` clause, and it still accepts it. + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: length + :end-before: //SNIPPET_END: length + +For that matter, you can leave out the ``decreases`` clause in +``ackermann`` and F* is okay with it. + +.. code-block:: fstar + + let rec ackermann (m n:nat) + : nat + = if m=0 then n + 1 + else if n = 0 then ackermann (m - 1) 1 + else ackermann (m - 1) (ackermann m (n - 1)) + +This is because F* uses a simple heuristic to choose the decreases +clause, if the user didn't provide one. + +The *default* decreases clause for a total, recursive function is the +lexicographic ordering of all the non-function-typed arguments, taken +in order from left to right. + +That is, the default decreases clause for ``ackermann`` is exactly +``decreases %[m; n]``; and the default for ``length`` is just +``decreases %[a; l]`` (which is equivalent to ``decreases l``). So, you +needn't write it. + +On the other hand, it you were to flip the order of arguments to +``ackermann``, then the default choice of the measure would not be +correct—so, you'll have to write it explicitly, as shown below. + +.. literalinclude:: ../code/Part1.Termination.fst + :language: fstar + :start-after: SNIPPET_START: ackermann_flip + :end-before: SNIPPET_END: ackermann_flip + +.. _Part1_mutual_recursion: + +Mutual recursion +^^^^^^^^^^^^^^^^ + +F* also supports mutual recursion and the same check of proving that a +measure of the arguments decreases on each (mutually) recursive call +applies. + +For example, one can write the following code to define a binary +``tree`` that stores an integer at each internal node—the keyword +``and`` allows defining several types that depend mutually on each +other. + +To increment all the integers in the tree, we can write the mutually +recursive functions, again using ``and`` to define ``incr_tree`` and +``incr_node`` to depend mutually on each other. F* is able to prove +that these functions terminate, just by using the default measure as +usual. + +.. literalinclude:: ../code/Part1.Termination.fst + :language: fstar + :start-after: //SNIPPET_START: incr_tree + :end-before: //SNIPPET_END: incr_tree + +.. note:: + + Sometimes, a little trick with lexicographic orderings can help + prove mutually recursive functions correct. We include it here as a + tip, you can probably skip it on a first read. + + .. literalinclude:: ../code/Part1.Termination.fst + :language: fstar + :start-after: SNIPPET_START: foo_bar + :end-before: SNIPPET_END: foo_bar + + What's happening here is that when ``foo l`` calls ``bar``, the + argument ``xs`` is legitimately a sub-term of ``l``. However, ``bar + l`` simply calls back ``foo l``, without decreasing the + argument. The reason this terminates, however, is that ``bar`` can + freely call back ``foo``, since ``foo`` will only ever call ``bar`` + again with a smaller argument. You can convince F* of this by + writing the decreases clauses shown, i.e., when ``bar`` calls + ``foo``, ``l`` doesn't change, but the second component of the + lexicographic ordering does decrease, i.e., ``0 << 1``. + + +The termination check, precisely +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Having seen a few examples at work, we can now describe how the +termination check works in general. + + +.. note:: + + We use a slightly more mathematical notation here, so that we can + be precise. If it feels unfamiliar, you needn't understand this + completely at first. Continue with the examples and refer back to + this section, if and when you feel like a precise description would + be helpful. + +When defining a recursive function + +.. math:: + + \mathsf{f~(\overline{x:t})~:~Tot~r~(decreases~m)~=~e} + +i.e., :math:`\mathsf{f}` is a function with several arguments +:math:`\mathsf{x1:t1}, ..., \mathsf{x_n:t_n}`, returning +:math:`\mathsf{r}` with measure :math:`\mathsf{m}`, mutually +recursively with other functions of several arguments at type: + +.. math:: + + \mathsf{f_1~(\overline{x_1:t_1})~:~Tot~r_1~(decreases~m_1)} \\ + \ldots \\ + \mathsf{f_n~(\overline{x_n:t_n})~:~Tot~r_n~(decreases~m_n)} \\ + +we check the definition of the function body of :math:`\mathsf{f}` +(i.e., :math:`\mathsf{e}`) with all the mutually recursive functions +in scope, but at types that restrict their domain, in the following +sense: + +.. math:: + + \mathsf{f~:~(\overline{y:t}\{~m[\overline{y}/\overline{x}]~<<~m~\}~\rightarrow~r[\overline{y}/\overline{x}])} \\ + \mathsf{f_1~:~(\overline{x_1:t_1}\{~m_1~<<~m~\}~\rightarrow~r_1)} \\ + \ldots \\ + \mathsf{f_n~:~(\overline{x_n:t_n}\{~m_n~<<~m~\}~\rightarrow~r_n)} \\ + +That is, each function in the mutually recursive group can only be +applied to arguments that precede the current formal parameters of +:math:`\mathsf{f}` according to the annotated measures of each +function. + +.. _Part1_termination_fibonacci: + +Exercise: Fibonacci in linear time +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`Click here <../code/exercises/Part1.Termination.fst>`_ for the exercise file. + +Here's a function to compute the :math:`n`-th Fibonacci number. + +.. code-block:: fstar + + let rec fibonacci (n:nat) + : nat + = if n <= 1 + then 1 + else fibonacci (n - 1) + fibonacci (n - 2) + +Here's a more efficient, tail-recursive, linear-time variant. + +.. code-block:: fstar + + let rec fib a b n = + match n with + | 0 -> a + | _ -> fib b (a+b) (n-1) + + let fibonacci n = fib 1 1 n + +Add annotations to the functions to get F* to accept them, in +particular, proving that ``fib`` terminates. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Termination.fst + :language: fstar + :start-after: SNIPPET_START: fib + :end-before: SNIPPET_END: fib + + +.. _Part1_termination_reverse: + +Exercise: Tail-recursive reversal +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`Click here <../code/exercises/Part1.Termination.fst>`_ for the exercise file. + +Here is a function to reverse a list: + +.. code-block:: fstar + + let rec rev #a (l:list a) + : list a + = match l with + | [] -> [] + | hd::tl -> append (rev tl) hd + +But, it is not very efficient, since it is not tail recursive and, +worse, it is quadratic, it traverses the reversed tail of the list +each time to add the first element to the end of it. + +This version is more efficient, because it is tail recursive and +linear. + +.. code-block:: fstar + + let rec rev_aux l1 l2 = + match l2 with + | [] -> l1 + | hd :: tl -> rev_aux (hd :: l1) tl + + let rev l = rev_aux [] l + +Add type annotations to ``rev_aux`` and ``rev``, proving, in +particular, that ``rev_aux`` terminates. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part1.Termination.fst + :language: fstar + :start-after: SNIPPET_START: rev + :end-before: SNIPPET_END: rev diff --git a/book/part1/part1_wrap.rst b/book/part1/part1_wrap.rst new file mode 100644 index 00000000000..39c31a73938 --- /dev/null +++ b/book/part1/part1_wrap.rst @@ -0,0 +1,35 @@ +.. _Part1_wrap: + +Wrapping up +=========== + +Congratulations! You've reached the end of an introduction to basic F*. + +You should have learned the following main concepts: + +* Basic functional programming +* Using types to write precise specifications +* Writing proofs as total functions +* Defining and working with new inductive types +* Lemmas and proofs by induction + +Throughout, we saw how F*'s use of an SMT solver can reduce the +overhead of producing proofs, and you should know enough now to +be productive in small but non-trivial F* developments. + +However, it would be wrong to conclude that SMT-backed proofs in F* +are all plain sailing. And there's a lot more to F* than SMT +proofs---so read on through the rest of this book. + +But, if you do plan to forge ahead with mainly SMT-backed proofs, you +should keep the following in mind before attempting more challenging +projects. + +It'll serve you well to learn a bit more about how an SMT solver works +and how F* interfaces with it---this is covered in a few upcoming +sections, including a section on :ref:`classical proofs +` and in :ref:`understanding how F* uses Z3 +`. Additionally, if you're interested in doing proofs about +arithmetic, particularly nonlinear arithmetic, before diving in, you +would do well to read more about the F* library ``FStar.Math.Lemmas`` +and F* arithmetic settings. diff --git a/book/part2/part2.rst b/book/part2/part2.rst new file mode 100644 index 00000000000..133cf34a997 --- /dev/null +++ b/book/part2/part2.rst @@ -0,0 +1,172 @@ +.. _Part2: + +################################################################ +Representing Data, Proofs, and Computations with Inductive Types +################################################################ + + +.. + In this second part of the book, we'll dive deeper into F*, focusing + on *inductive definitions*, the main mechanism in F* for the user to + define new types. + +Earlier, we learned about :ref:`defining new data types ` +in F*. For example, here's the type of lists parameterized by a type +``a`` of the list elements. + +.. code-block:: fstar + + type list a = + | Nil : list a + | Cons : hd:a -> tl:list a -> list a + +We also saw that it was easy to define basic functions over these +types, using pattern matching and recursion. For example, here's +a function to compute the length of a list. + +.. literalinclude:: ../code/Part1.Inductives.fst + :language: fstar + :start-after: //SNIPPET_START: length + :end-before: //SNIPPET_END: length + +The function ``length`` defines some property of a ``list`` (its +length) separately from the definition of the ``list`` type itself. +Sometimes, however, it can be convenient to define a property of a +type together with the type itself. For example, in some situations, +it may be natural to define the length of the list together with the +definition of the list type itself, so that every list is structurally +equipped with a notion of its length. Here's how: + +.. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: vec + :end-before: SNIPPET_END: vec + +What we have here is our first indexed type, ``vec a n``. One way to +understand this definition is that ``vec a : nat -> Type`` describes a +family of types, ``vec a 0``, ``vec a 1``, ... etc., all representing +lists of ``a``-typed elements, but where the *index* ``n`` describes +the length of the list. With this definition of ``vec``, the function +``length`` is redundant: given a ``v : vec a n`` we know that its +``length v`` is ``n``, without having to recompute it. + +This style of enriching a type definition with indexes to state +properties of the type is reminiscent of what we learned earlier about +:ref:`intrinsic versus extrinsic proofs +`. Rather than defining a single type +``list a`` for all lists and then separatately (i.e., extrinsically) +defining a function ``length`` to compute the length of a list, with +``vec`` we've enriched the type of the list intrinsically, so that +type of ``vec`` immediately tells you its length. + +Now, you may have seen examples like this length-indexed ``vec`` type +before---it comes up often in tutorials about dependently typed +programming. But, indexed types can do a lot more. In this section we +learn about indexed inductive types from three related perspectives: + + * Representing data: Inductive types allow us to build new data + types, includes lists, vectors, trees, etc. in several flavors. + We present two case studies: :ref:`vectors ` and + :ref:`Merkle trees `, a binary tree data structure + equipped with cryptographic proofs. + + * Representing proofs: The core logic of F* rests upon several + simple inductive type definitions. We revisit the logical + connectives we've seen before (including the :ref:`propositional + connectives ` and :ref:`equality + `) and show how rather than being primitive + notions in F*, their definitions arise from a few core + constructions involving inductive type. Other core notions in the + language, including the handling of :ref:`termination proofs + `, can also be understood in terms of inductive + types that :ref:`model well-founded recursion + `. + + * Representing computations: Inductive type definitions allow + embedding other programming languages or computational models + within F*. We develop two case studies. + + + We develop a :ref:`deep embedding of the simply-typed lambda + calculus ` with several reduction strategies, and a + proof of its syntactic type soundness. The example showcases the + use of several inductive types to represent the syntax of a + programming language, a relation describing its type system, and + another relation describing its operational semantics. + + + We also show how to use :ref:`higher-order abstract syntax + ` to represent well-typed lambda terms, a concise + style that illustrates how to use inductive types that store + functions. + + + Finally, we look at a :ref:`shallow embedding of an imperative + programming language with structured concurrency `, + representing computations as infinitely branching inductively + defined trees. The example introduces modeling computational + effects as monads and showcases the use of inductive types + at higher order. + +This section is somewhat more advanced than the first. It also +interleaves some technical material about F*'s core logic with case +studies showing some of those core concepts at work. You can certainly +work through the material sequentially, but depending on your +interests, you may find the following paths through the material to be +more accessible. + +If you're familiar with dependent types but are new to F* and want a +quick tour, the following path might work for you: + + * :ref:`Length-indexed lists `, F*-specific notations + + * :ref:`Equality ` + + * :ref:`Logical connectives ` + + * Any of the case studies, depending on your interest. + +If you're unfamiliar with dependent types and are more curious to +learn how to use F* by working through examples, following path might +work for you: + + * :ref:`Inductive type definitions `, basic concepts + + * :ref:`Length-indexed lists `, F*-specific notations in the simplest setting + + * :ref:`Merkle trees `, a more interesting example, with applications to cryptographic security + + * :ref:`Logical connectives `, some utilities to manipulate F*'s logical connectives + + * Any of the case studies, depending on your interest, with the :ref:`Simply Typed Lambda Calculus ` perhaps the easiest of them. + +But, by the end of this section, through several exercises, we expect +the reader to be familiar enough with inductive types to define their +own data structures and inductively defined relations, while also +gaining a working knowledge of some core parts of F*'s type theory. + + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + part2_inductive_type_families + part2_vectors + part2_merkle + part2_equality + part2_logical_connectives + part2_stlc + part2_phoas + part2_well_founded + part2_par + part2_universes + +.. + Vectors for basics + - But vectors are too simple, we can do them with just refined lists + + Merkle trees to capture more interesting invariants of a type + + Higher-order inductive types: infinitely branching trees + - Free monads and computation trees + + Representing proof terms: Simply-typed lambda calculus + + Representing proof terms: Accessibility predicates and termination proofs diff --git a/book/part2/part2_equality.rst b/book/part2/part2_equality.rst new file mode 100644 index 00000000000..0054f02c6c7 --- /dev/null +++ b/book/part2/part2_equality.rst @@ -0,0 +1,453 @@ +.. _Part2_equality: + +Equality Types +============== + +In an :ref:`early section ` we learned that F* +supports at least two kinds of equality. In this section, we look in +detail at definitional equality, propositional equality, extensional +equality of functions, and decidable equality. These topics are fairly +technical, but are core features of the language and their treatment +in F* makes essential use of an indexed inductive type, ``equals #t x +y``, a proposition asserting the equality of ``x:t`` and ``y:t``. + +Depending on your level of comfort with functional programming and +dependent types, you may want to skip or just skim this chapter on a +first reading, returning to it for reference if something is unclear. + +Definitional Equality +..................... + +One of the main distinctive feature of a type theory like F* (or Coq, +Lean, Agda etc., and in contrast with foundations like set theory) is +that *computation* is a primitive notion within the theory, such that +lambda terms that are related by reduction are considered +identical. For example, there is no way to distinguish within the +theory between :math:`(\lambda x.x) 0` and :math:`0`, since the former +reduces in a single step of computation to the latter. Terms that are +related by reduction are called *definitionally equal*, and this is +the most primitive notion of equality in the language. Definitional +equality is a congruence, in the sense that within any context +:math:`T[]`, :math:`T[n]` is definitionally equal to :math:`T[m]`, +when :math:`n` and :math:`m` are definitionally equal. + +Since definitionally equal terms are identical, all type theories, +including F*, will implicit allow treating a term ``v:t`` as if it had +type ``t'``, provided ``t`` and ``t'`` are definitionally equal. + +Let's look at a few examples, starting again with our type of +length-indexed vectors. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: vec$ + :end-before: //SNIPPET_END: vec$ + +As the two examples below show a ``v:vec a n`` is also has type ``vec +a m`` when ``n`` and ``m`` are definitionally equal. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: vec_conversions$ + :end-before: //SNIPPET_END: vec_conversions$ + +In the first case, a single step of computation (a function +application, or :math:`\beta`-reduction) suffices; while the second +case requires a :math:`\beta`-reduction followed by a step of integer +arithmetic. In fact, any computational step, including unfolding +defintions, conditionals, fixpoint reduction etc. are all allowed when +deciding if terms are definitionally equivalent---the code below +illustrates how F* implicitly reduces the ``factorial`` function when +deciding if two terms are definitionally equal. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: vec_conversions_fact$ + :end-before: //SNIPPET_END: vec_conversions_fact$ + +Of course, there is nothing particularly special about the ``vec`` +type or its indices. Definitional equality applies everywhere, as +illustrated below. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: conv_int$ + :end-before: //SNIPPET_END: conv_int$ + +Here, when adding ``1`` to ``x``, F* implicitly converts the type of +``x`` to ``int`` by performing a :math:`\beta`-reduction followed by a +case analysis. + +Propositional Equality +...................... + +Definitional equality is so primitive in the language that there is no +way to even state within the terms that two terms are definitional +equal, i.e., there is no way to state within the logic that two terms +are related to each other by reduction. The closest one can get +stating that two terms are equal is through a notion called a +*provable equality* or propositional equality. + +In thinking of propositions as types, we mentioned at the :ref:`very +start of the book `, that one can think of a type ``t`` as a +proposition, or a statement of a theorem, and ``e : t`` as a proof of +the theorem ``t``. So, one might ask, what type corresponds to the +equality proposition and how are proofs of equality represented? + +The listing below shows the definition of an inductive type ``equals +#a x y`` representing the equality proposotion between ``x:a`` and +``y:a`` . Its single constructor ``Reflexivity`` is an equality proof. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: equals$ + :end-before: //SNIPPET_END: equals$ + +Its easy to construct some simple equality proofs. In the second case, +just as with our vector examples, F* accepts ``Reflexivity #_ #6`` as +having type ``equals (factorial 3) 6``, since ``equals 6 6`` is +definitionally equal to ``equals (factorial 3) 6``. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: sample_equals_proofs$ + :end-before: //SNIPPET_END: sample_equals_proofs$ + +Although the only constructor of ``equals`` is ``Reflexivity``, as the +the following code shows, ``equals`` is actually an equivalence +relation, satisfying (in addition to reflexivity) the laws of symmetry +and transitivity. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: equivalence_relation$ + :end-before: //SNIPPET_END: equivalence_relation$ + +This might seem like magic: how is it is that we can derive symmetry +and transitivity from reflexivity alone? The answer lies in how F* +interprets inductive type definitions. + +In particular, given an inductive type definition of type +:math:`T~\overline{p}`, where :math:`\overline{p}` is a list of +parameters and, F* includes an axiom stating that any value :math:`v: +T~\overline{p}` must be an application of one of the constructors of +:math:`T`, :math:`D~\overline{v} : T~\overline{p'}`, such that +:math:`\overline{p} = \overline{p'}`. + +In the case of equality proofs, this allows F* to conclude that every +equality proof is actually an instance of ``Reflexivity``, as shown +below. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: uip_refl$ + :end-before: //SNIPPET_END: uip_refl$ + +Spend a minute looking at the statement above: the return type is a +statement of equality about equality proofs. Write down a version of +``uip_refl`` making all implicit arguments explicit. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: uip_refl_explicit$ + :end-before: //SNIPPET_END: uip_refl_explicit$ + +-------------------------------------------------------------------------------- + +In fact, from ``uip_refl``, a stronger statement showing that all +equality proofs are equal is also provable. The property below is +known as the *uniqueness of identity proofs* (UIP) and is at the core +of what makes F* an extensional type theory. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: uip$ + :end-before: //SNIPPET_END: uip$ + +The F* module ``Prims``, the very first module in every program's +dependence graph, defines the ``equals`` type as shown here. The +provable equality predicate ``(==)`` that we've used in several +examples already is just a squashed equality proof, as shown below. + +.. code-block:: fstar + + let ( == ) #a (x y : a) = squash (equals x y) + +In what follows, we'll mostly use squashed equalities, except where we +wish to emphasize the reflexivity proofs. + +Equality Reflection +................... + +What makes F* an *extensional* type theory (and unlike the +*intensional* type theories implemented by Coq, Lean, Agda, etc.) is a +feature known as equality reflection. Whereas intensional type +theories treat definitional and provable equalities separate, in F* +terms that are provably equal are also considered definitionally +equal. That is, if in a given context ``x == y`` is derivable, the +``x`` is also definitionally equal to ``y``. This has some +wide-reaching consequences. + +Implicit conversions using provable equalities +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Recall from the start of the chapter that ``v:vec a ((fun x -> x) 0)`` +is implicitly convertible to the type ``vec a 0``, since the two types +are related by congruence and reduction. However, as the examples +below show, if ``a == b`` is derivable in the context, then +``v:a`` can be implicity converted to the type ``b``. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: conversion_with_equality_proofs$ + :end-before: //SNIPPET_END: conversion_with_equality_proofs$ + +We do not require a proof of ``a == b`` to be literally bound in the +context. As the example below shows, the hypothesis ``h`` is used in +conjunction with the control flow of the program to prove that in the +``then`` branch ``aa : int`` and in the ``else`` branch ``bb : int``. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: conversion_complex$ + :end-before: //SNIPPET_END: conversion_complex$ + +In fact, with our understanding of equality proofs, we can better +explain how case analysis works in F*. In the code above, the +``then``-branch is typechecked in a context including a hypothesis +``h_then: squash (equals (x > 0) true)``, while the ``else`` branch +includes the hypothesis ``h_else: squash (equals (x > 0) false)``. The +presence of these additional control-flow hypotheses, in conjunction +with whatever else is in the context (in particular hypothesis ``h``) +allows us to derive ``(a == int)`` and ``(b == int)`` in the +respective branches and convert the types of ``aa`` and ``bb`` +accordingly. + +Undecidability and Weak Normalization +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Implicit conversions with provable equalities are very convenient---we +have relied on it without noticing in nearly all our examples so far, +starting from the simplest examples about lists to vectors and Merkle +trees, and some might say this is the one key feature which gives F* +its programming-oriented flavor. + +However, as the previous example hinted, it is, in general, +undecidable to determine if ``a == b`` is derivable in a given +context. In practice, however, through the use of an SMT solver, F* +can often figure out when terms are provably equal and convert using +it. But, it cannot always do this. In such cases, the F* standard +library offers the following primitive (in FStar.Pervasives), which +allows the user to write ``coerce_eq pf x``, to explicitly coerce the +type of ``x`` using the equality proof ``pf``. + +.. code-block:: fstar + + let coerce_eq (#a #b:Type) (_:squash (a == b)) (x:a) : b = x + +Another consequence of equality reflection is the loss of strong +normalization. Intensional type theories enjoy a nice property +ensuring that every term will reduce to a canonical normal form, no +matter the order of evaluation. F* does not have this property, since +some terms, under certain evaluation orders, can reduce +infinitely. However, metatheory developed for F* proves that closed +terms (terms without free variables) in the ``Tot`` effect do not +reduce infinitely, and as a corollary, there are no closed proofs of +``False``. + +F* includes various heuristics to avoid getting stuck in an infinite +loop when reducing open terms, but one can craft examples to make F*'s +reduction macinery loop forever. As such, deciding if possibly open +terms have the same normal form is also undecidable in F*. + +.. _Part2_funext: + +Functional Extensionality +......................... + +Functional extensionality is a principle that asserts the provable +equality of functions that are pointwise equal. That is, for functions +:math:`f` and :math:`g`, :math:`\forall x. f x == g x` implies +:math:`f == g`. + +This principle is provable as a theorem in F*, but only for function +literals, or, equivalently, :math:`\eta`-expanded functions. That is, +the following is a theorem in F*. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: funext_eta$ + :end-before: //SNIPPET_END: funext_eta$ + +.. note:: + + Note, the proof of the theorem makes use of tactics, a topic we'll + cover in a later chapter. You do not need to understand it in + detail, yet. The proof roughly says to descend into every sub-term + of the goal and try to rewrite it using the pointwise equality + hypothesis ``hyp``, and if it fails to just rewrite the sub-term to + itself. + +Unfortunately, functional extensionality does not apply to all +functions. That is, the following is not provable in F* nor is it +sound to assume it as an axiom. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: funext$ + :end-before: //SNIPPET_END: funext$ + +The problem is illustrated by the following counterexample, which +allows deriving ``False`` in a context where ``funext`` is valid. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: funext_false$ + :end-before: //SNIPPET_END: funext_false$ + +The proof works by exploiting the interaction with refinement +subtyping. ``f`` and ``g`` are clearly not pointwise equal on the +entire domain of natural numbers, yet they are pointwise equal on the +positive natural numbers. However, from ``ax #pos f g`` we gain that +``f == g``, and in particular that ``f 0 == g 0``, which is false. + +.. note:: + + The trouble arises in part because although ``ax:funext`` proves + ``squash (equals #(pos -> int) f g)``, F*'s encoding of the + equality to the SMT solver (whose equality is untyped) treats the + equality as ``squash (equals #(nat -> int) f g)``, which leads to + the contradiction. + +Further, :math:`\eta`-equivalent functions in F* are not considered +provably equal. Otherwise, in combination with ``funext_on_eta``, an +:math:`\eta`-equivalence principle leads to the same contradiction as +``funext_false``, as shown below. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: eta_equiv_false$ + :end-before: //SNIPPET_END: eta_equiv_false$ + +The F* standard library module ``FStar.FunctionalExtensionality`` +provides more information and several utilities to work with +functional extensionality on :math:`\eta`-expanded functions. + +Thanks in particular to Aseem Rastogi and Dominique Unruh for many +insights and discussions related to functional extensionality. + +Exercise +........ + +Leibniz equality ``leq x y``, relates two terms ``x:a`` and ``y:a`` if +for all predicates ``p:a -> Type``, ``p a`` implies ``p b``. That is, +if no predicate can distinguish ``x`` and ``y``, the they must be +equal. + +Define Leibniz equality and prove that it is an equivalence relation. + +Then prove that Leibniz equality and the equality predicate ``equals x +y`` defined above are isomorphic, in the sense that ``leq x y -> +equals x y`` and ``equals x y -> leq x y``. + +`Exercise file <../code/exercises/Part2.Leibniz.fst>`_ + +.. container:: toggle + + .. container:: header + + **Hint** + + The section on Leibniz equality `here + `_ tells you how to do it in + Agda. + + .. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: leibniz$ + :end-before: //SNIPPET_END: leibniz$ + +-------------------------------------------------------------------------------- + +.. _Part2_equality_qualifiers: + +Decidable equality and equality qualifiers +.......................................... + +To end this chapter, we discuss a third kind of equality in F*, the +polymorphic *decidable equality* with the signature shown below taken +from the the F* module ``Prims``. + +.. code-block:: fstar + + val ( = ) (#a:eqtype) (x y:a) : bool + +On ``eqtype``, i.e., ``a:Type{hasEq a}``, decidable quality ``(=)`` +and provable equality coincide, as shown below. + +.. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: dec_equals_dec$ + :end-before: //SNIPPET_END: dec_equals_dec$ + +That is, for the class of ``eqtype``, ``x = y`` returns a boolean +value that decides equality. Decidable equality and ``eqtype`` were +first covered in :ref:`an earlier chapter `, where we +mentioned that several primitive types, like ``int`` and ``bool`` all +validate the ``hasEq`` predicate and are, hence, instances of ``eqtype``. + +When introducing a new inductive type definition, F* tries to +determine whether or not the type supports decidable equality based on +a structural equality of the representation of the values of that +type. If so, the type is considered an ``eqtype`` and uses of the ``( += )`` operator are compiled at runtime to structural comparison of +values provided by the target language chosen, e.g., OCaml, F\#, or C. + +The criterion used to determine whether or not the type supports +equality decidable is the following. + +Given an inductive type definition of :math:`T` with parameters +:math:`\overline{p}` and indexes :math:`~\overline{q}`, for each +constructor of :math:`D` with arguments :math:`\overline{v:t_v}`, + +1. Assume, or every type parameter :math:`t \in \overline{p}`, :math:`\mathsf{hasEq}~t`. + +2. Assume, for recursive types, for all :math:`\overline{q}`, :math:`\mathsf{hasEq}~(T~\overline{p}~\overline{q})`. + +3. Prove, for all arguments :math:`\overline{v:t_v}`, prove :math:`\mathsf{hasEq}~t_v`. + +If the proof in step 3 suceeds for all constructors, then F* +introduces an axiom +:math:`\forall~\overline{p}~\overline{q}. (\forall t \in \overline{p}. \mathsf{hasEq}~t) \Rightarrow \mathsf{hasEq}~(T~\overline{p}~\overline{q})`. + +If the check in step 3 fails for any constructor, F* reports an error +which the user can address by adding one of two qualifiers to the type. + +1. ``noeq``: This qualifier instructs F* to consider that the type + does not support decidable equality, e.g., if one of the + constructors contains a function, as show below. + + .. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: noeq$ + :end-before: //SNIPPET_END: noeq$ + +2. ``unopteq``: This qualifier instructs F* to determine whether a + given instance of the type supports equality, even when some of its + parameters are not themselves instances of ``eqtype``. This can be + useful in situations such as the following: + + .. literalinclude:: ../code/ProvableEquality.fst + :language: fstar + :start-after: //SNIPPET_START: unopteq$ + :end-before: //SNIPPET_END: unopteq$ + +This `wiki page +`_ +provides more information about equality qualifiers on inductive types. diff --git a/book/part2/part2_inductive_type_families.rst b/book/part2/part2_inductive_type_families.rst new file mode 100644 index 00000000000..ceeb87c4594 --- /dev/null +++ b/book/part2/part2_inductive_type_families.rst @@ -0,0 +1,251 @@ +.. _Part2_inductives: + +Inductive type definitions +========================== + +An inductive type definition, sometimes called a *datatype*, has the +following general structure. + +.. math:: + + \mathsf{type}~T_1~\overline{(x_1:p_1)} : \overline{y_1:q_1} \rightarrow \mathsf{Type} = \overline{| D_1 : t_1} \\ + \mathsf{and}~T_n~\overline{(x_n:p_n)} : \overline{y_n:q_n} \rightarrow \mathsf{Type} = \overline{| D_n : t_n} \\ + +This defines :math:`n` mutually inductive types, named :math:`T_1 \ldots +T_n`, called the *type constructors*. Each type constructor :math:`T_i` +has a number of *parameters*, the :math:`\overline{x_i : p_i}`, and a +number of *indexes*, the :math:`\overline{y_i:q_i}`. + +Each type constructor :math:`T_i` has zero or more *data constructors* +:math:`\overline{D_i:t_i}`. For each data constructor :math:`D_{ij}`, its +type :math:`t_{ij}` must be of the form :math:`\overline{z:s} \rightarrow +T_i~\bar{x_i}~\bar{e}`, i.e., it must be a function type returning an +instance of :math:`T_i` with *the same parameters* +:math:`\overline{x_i}` as in the type constructor's signature, but with +any other well-typed terms :math:`\overline{e}` for the index +arguments. This is the main difference between a parameter and an +index—a parameter of a type constructor *cannot* vary in the result +type of the data constructors, while the indexes can. + +Further, in each of the arguments :math:`\overline{z:s}` of the data +constructor, none of the mutually defined type constructors +:math:`\overline{T}` may appear to the left of an arrow. That is, all +occurrences of the type constructors must be *strictly positive*. This +is to ensure that the inductive definitions are well-founded, as +explained below. Without this restriction, it is easy to break +soundness by writing non-terminating functions with ``Tot`` types. + +Also related to ensuring logical consistency is the *universe* level +of an inductive type definition. We'll return to that later, once +we've done a few examples. + +.. _Part2_strict_positivity: + +Strictly positive definitions ++++++++++++++++++++++++++++++ + +As a strawman, consider embedding a small dynamically typed +programming language within F*. All terms in our language have the +same static type ``dyn``, although at runtime values could have +type ``Bool``, or ``Int``, or ``Function``. + +One attempt at representing a language like this using a data type in +F* is as follows: + +.. literalinclude:: ../code/Part2.Positivity.fst + :language: fstar + :start-after: //SNIPPET_START: dyn$ + :end-before: //SNIPPET_END: dyn$ + +The three cases of the data type represent our three kinds of runtime +values: ``Bool b``, ``Int b``, and ``Function f``. The ``Function`` +case, however, is problematic: The argument ``f`` is itself a function +from ``dyn -> dyn``, and the constructor ``Function`` allows promoting +a ``dyn -> dyn`` function into the type ``dyn`` itself, e.g., one can +represent the identity function in ``dyn`` as ``Function (fun (x:dyn) +-> x)``. However, the ``Function`` case is problematic: as we will see +below, it allows circular definitions that enable constructing +instances of ``dyn`` without actually providing any base case. F* +rejects the definition of ``dyn``, saying "Inductive type dyn does not +satisfy the strict positivity condition". + +Consider again the general shape of an inductive type definition: + +.. math:: + + \mathsf{type}~T_1~\overline{(x_1:p_1)} : \overline{y_1:q_1} \rightarrow \mathsf{Type} = \overline{| D_1 : t_1} \\ + \mathsf{and}~T_n~\overline{(x_n:p_n)} : \overline{y_n:q_n} \rightarrow \mathsf{Type} = \overline{| D_n : t_n} \\ + +This definition is strictly positive when + + * for every type constructor :math:`T \in T_1, ..., T_n`, + + * and every data constructor :math:`D : t \in \overline{D_1}, + ... \overline{D_n}`, where `t` is of the form + :math:`x0:s_0 \rightarrow ... \rightarrow xn:s_n \rightarrow T_i ...`, + and :math:`s_0, ..., s_n` are the types of the fields of :math:`D` + + * and for all instantiations :math:`\overline{v}` of the type parameters + :math:`\overline{p}` of the type :math:`T`, + + * :math:`T` does not appear to the left of any arrow in any + :math:`s \in (s_0, ..., s_k)[\overline{v}/\overline{p}]`. + +Our type ``dyn`` violates this condition, since the defined typed +``dyn`` appears to the left of an arrow type in the ``dyn -> +dyn``-typed field of the ``Function`` constructor. + +To see what goes wrong if F* were to accept this definition, we can +suppress the error reported by using the option ``__no_positivity`` +and see what happens. + +.. literalinclude:: ../code/Part2.Positivity.fst + :language: fstar + :start-after: //SNIPPET_START: nopos_dyn$ + :end-before: //SNIPPET_END: nopos_dyn$ + +.. note:: + + F* maintains an internal stack of command line options. The + ``#push-options`` pragma pushes additional options at the top of + the stack, while ``#pop-options`` pops the stack. The pattern used + here instructs F* to typecheck ``dyn`` only with the + ``__no_positivity`` option enabled. As we will see, the + ``__no_positivity`` option can be used to break soundness, so use + it only if you really know what you're doing. + +Now, having declared that ``dyn`` is a well-formed inductive type, +despite not being strictly positive, we can break the soundness of +F*. In particular, we can write terms and claim they are total, when +in fact their execution will loop forever. + +.. literalinclude:: ../code/Part2.Positivity.fst + :language: fstar + :start-after: //SNIPPET_START: nopos_dyn_loop$ + :end-before: //SNIPPET_END: nopos_dyn_loop$ + +Here, the type of ``loop`` claims that it is a term that always +evaluates in a finite number of steps to a value of type ``dyn``. Yet, +reducing it produces an infinite chain of calls to ``loop' +(Function loop')``. Admitting a non-positive definition like ``dyn`` +has allowed us to build a non-terminating loop. + +Such loops can also allow one to prove ``False``, as the next example +shows. + +.. literalinclude:: ../code/Part2.Positivity.fst + :language: fstar + :start-after: //SNIPPET_START: non_positive$ + :end-before: //SNIPPET_END: non_positive$ + +This example is very similar to ``dyn``, except ``NP`` stores a +non-positive function that returns ``False``, which allows use to +prove ``ff : False``, i.e., in this example, not only does the +violation of strict positivity lead to an infinite loop at runtime, it +also renders the entire proof system of F* useless, since one can +prove ``False``. + +Finally, in the example below, although the type ``also_non_pos`` does +not syntactically appear to the left of an arrow in a field of the +``ANP`` constructor, an instantiation of the type parameter ``f`` +(e.g., with the type ``f_false``) does make it appear to the left of +an arrow---so this type too is deemed not strictly positive, and can be used +to prove ``False``. + +.. literalinclude:: ../code/Part2.Positivity.fst + :language: fstar + :start-after: //SNIPPET_START: also_non_positive$ + :end-before: //SNIPPET_END: also_non_positive$ + +We hope you are convinced that non-strictly positive types should not +be admissible in inductive type definitions. In what follows, we will +no longer use the ``__no_positivity`` option. In a later section, once +we've introduced the *effect of divergence*, we will see that +non-positive definitions can safely be used in a context where +programs are not expected to terminate, allowing one to safely model +things like the ``dyn`` type, without compromising the soundness of +F*. + +.. _Part2_strictly_positive_annotations: + +Strictly Positive Annotations +----------------------------- + +Sometimes it is useful to parameterize an inductive definition with a +type function, without introducing a non-positive definition as we did +in ``also_non_pos`` above. + +For example, the definition below introduces a type ``free f a``, a +form of a tree whose leaf nodes contain ``a`` values, and whose +internal nodes branch according the type function ``f``. + +.. literalinclude:: ../code/Part2.Positivity.fst + :language: fstar + :start-after: //SNIPPET_START: free$ + :end-before: //SNIPPET_END: free$ + +We can instantiate this generic ``free`` to produce various kinds of +trees. Note: when instantiating ``free list a`` in +``variable_branching_list`` below, we need to explicitly re-define the +``list`` type with a strict-positivity annotation: F* does not +correctly support rechecking type constructors to prove that they are +strictly positive when they are used at higher order. + +.. literalinclude:: ../code/Part2.Positivity.fst + :language: fstar + :start-after: //SNIPPET_START: free_instances$ + :end-before: //SNIPPET_END: free_instances$ + +However, we should only be allowed to instantate ``f`` with type +functions that are strictly positive in their argument, since otherwise +we can build a proof of ``False``, as we did with +``also_non_pos``. The ``@@@strictly_positive`` attribute on the +formal parameter of ``f`` enforces this. + +If we were to try to instantiate ``free`` with a non-strictly positive +type function, + +.. literalinclude:: ../code/Part2.Positivity.fst + :language: fstar + :start-after: //SNIPPET_START: free_bad$ + :end-before: //SNIPPET_END: free_bad$ + +then F* raises an error: + +.. code-block:: + + Binder (t: Type) is marked strictly positive, but its use in the definition is not + +Unused Annotations +------------------ + +Sometimes one indexes a type by another type, though the index has no +semantic meaning. For example, in several F* developments that model +mutable state, the a heap reference is just a natural number modeling +its address in the heap. However, one might use the type ``let ref +(a:Type) = nat`` to represent the type of a reference, even though the +type ``a`` is not used in the definition. In such cases, it can be +useful to mark the parameter as unused, to inform F*'s positivity +checker that the type index is actually irrelevant. The snippet below +shows an example: + +.. literalinclude:: ../code/Part2.Positivity.fst + :language: fstar + :start-after: //SNIPPET_START: unused$ + :end-before: //SNIPPET_END: unused$ + +Here, we've marked the parameter of ``ref`` with the ``unused`` +attribute. We've also marked ``ref`` as ``irreducible`` just to +ensure for this example that F* does not silently unfold the +definition of ``ref``. + +Now, knowing that the parameter of ``ref`` is unused, one can define +types like ``linked_list a``, where although ``linked_list a`` appears +as an argument to the ``ref`` type, the positivity checker accepts it, +since the parameter is unused. This is similar to the use of a +``strictly_positive`` annotation on a parameter. + +However, with the ``unused`` attribute, one can go further: e.g., the +type ``neg_unused`` shows that even a negative occurrence of the +defined type is accepted, so long as it appears only as an +instantiation of an unused parameter. diff --git a/book/part2/part2_logical_connectives.rst b/book/part2/part2_logical_connectives.rst new file mode 100644 index 00000000000..5a024169bc9 --- /dev/null +++ b/book/part2/part2_logical_connectives.rst @@ -0,0 +1,634 @@ +.. _Part2_connectives: + +Constructive & Classical Connectives +==================================== + +In :ref:`an earlier chapter `, we learned +about the propositional connectives :math:`\forall, \exists, +\Rightarrow, \iff, \wedge, \vee, \neg`, etc. Whereas in other logical +frameworks these connectives are primitive, in a type theory like F* +these connectives are defined notions, built from inductive type +definitions and function types. In this section, we take a closer look +at these logical connectives, show their definitions, and present some +utilities to manipulate them in proofs. + +Every logical connective comes in two flavors. First, in its most +primitive form, it is defined as an inductive or arrow type, giving a +constructive interpretation to the connective. Second, and more +commonly used in F*, is a *squashed*, or proof-irrelevant, variant of +the same connective---the squashed variant is classical rather than +constructive and its proofs are typically derived by writing partial +proof terms with the SMT filling in the missing parts. + +Each connective has an *introduction* principle (which describes how +to build proofs of that connective) and an *elimination* principle +(which describes how to use a proof of that connective to build other +proofs). Example uses of introduction and elimination principles for +all the connectives can be found in `ClassicalSugar.fst +`_ + +All these types are defined in ``Prims``, the very first module in all +F* programs. + +Falsehood +......... + +The ``empty`` inductive type is the proposition that has no +proofs. The logical consistency of F* depends on there being no closed +terms whose type is ``empty``. + +.. code-block:: fstar + + type empty = + +This definition might look odd at first: it defines an inductive type +with *zero* constructors. This is perfectly legal in F*, unlike in +languages like OCaml or F#. + +The squashed variant of ``empty`` is called ``False`` and is defined +as shown below: + +.. code-block:: fstar + + let False = squash empty + +Introduction +++++++++++++ + +The ``False`` proposition has no introduction form, since it has no proofs. + +Elimination ++++++++++++ + +From a (hypothetical) proof of ``False``, one can build a proof of any +other type. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: empty_elim$ + :end-before: //SNIPPET_END: empty_elim$ + +This body of ``elim_false`` is a ``match`` expression with no branches, +which suffices to match all the zero cases of the ``empty`` type. + +``FStar.Pervasives.false_elim`` provides an analogous elimination rule +for ``False``, as shown below, where the termination check for the +recursive call succeeds trivially in a context with ``x:False``. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: false_elim$ + :end-before: //SNIPPET_END: false_elim$ + +Truth +..... + +The ``trivial`` inductive type has just a single proof, ``T``. + +.. code-block:: + + type trivial = T + +.. note:: + + Although isomorphic to the ``unit`` type with its single element + ``()``, for historic reasons, F* uses the ``trivial`` type to + represent trivial proofs. In the future, it is likely that + ``trivial`` will just be replaced by ``unit``. + +The squashed form of ``trivial`` is written ``True`` and is defined as: + +.. code-block:: + + let True = squash trivial + +Introduction +++++++++++++ + +The introduction forms for both the constructive and squashed variants +are trivial. + +.. code-block:: + + let _ : trivial = T + let _ : True = () + +Elimination ++++++++++++ + +There is no elimination form, since proofs of ``trivial`` are vacuous +and cannot be used to derive any other proofs. + + +Conjunction +........... + +A constructive proof of ``p`` and ``q`` is just a pair containing +proofs of ``p`` and ``q``, respectively. + +.. code-block:: + + type pair (p q:Type) = | Pair : _1:p -> _1:q -> pair p q + +.. note:: + + This type is isomorphic to the tuple type ``p & q`` that we + encountered previously :ref:`here `. F* currently + uses a separate type for pairs used in proofs and those used to + pair data, though there is no fundamental reason for this. In the + future, it is likely that ``pair`` will just be replaced by the + regular tuple type. + +The squashed form of conjunction is written ``/\`` and is defined as +follows: + +.. code-block:: + + let ( /\ ) (p q:Type) = squash (pair p q) + +Introduction +++++++++++++ + +Introducing a conjunction simply involves constructing a pair. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: and_intro$ + :end-before: //SNIPPET_END: and_intro$ + +To introduce the squashed version, there are two options. One can +either rely entirely on the SMT solver to discover a proof of ``p /\ +q`` from proofs of ``p`` and ``q``, which it is usually very capable +of doing. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: conj_intro$ + :end-before: //SNIPPET_END: conj_intro$ + +Or, if one needs finer control, F* offers specialized syntax +(defined in ``FStar.Classical.Sugar``) to manipulate each of the +non-trivial logical connectives, as shown below. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: conj_intro_sugar$ + :end-before: //SNIPPET_END: conj_intro_sugar$ + +The sugared introduction form for conjunction is, in general, as +follows: + +.. code-block:: fstar + + introduce p /\ q //Term whose top-level connective is /\ + with proof_of_p //proof_of_p : squash p + and proof_of_q //proof_of_q : squash q + +Elimination ++++++++++++ + +Eliminating a conjunction comes in two forms, corresponding to +projecting each component of the pair. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: and_elim$ + :end-before: //SNIPPET_END: and_elim$ + +For the squashed version, we again have two styles, the first relying +on the SMT solver. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: conj_elim$ + :end-before: //SNIPPET_END: conj_elim$ + +And a style using syntactic sugar: + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: conj_elim_sugar$ + :end-before: //SNIPPET_END: conj_elim_sugar$ + +Disjunction +........... + +A constructive proof of ``p`` or ``q`` is represented by the following +inductive type: + +.. code-block:: fstar + + type sum (p q:Type) = + | Left : p -> sum p q + | Right : q -> sum p q + +The constructors ``Left`` and ``Right`` inject proofs of ``p`` or +``q`` into a proof of ``sum p q``. + +.. note:: + + Just like before, this type is isomorphic to the type ``either p q`` + from ``FStar.Pervasives``. + +The classical connective ``\/`` described previously is just a +squashed version of ``sum``. + +.. code-block:: fstar + + let ( \/ ) (p q: Type) = squash (sum p q) + +Introduction +++++++++++++ + +As with the other connectives, introducing a constructive disjunction +is just a matter of using the ``Left`` or ``Right`` constructor. + +To introduce the squashed version ``\/``, one can either rely on the +SMT solver, as shown below. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: or_intro$ + :end-before: //SNIPPET_END: or_intro$ + +Or, using the following syntactic sugar, one can specifically provide +a proof for either the ``Left`` or ``Right`` disjunct. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: or_intro_sugar$ + :end-before: //SNIPPET_END: or_intro_sugar$ + +Elimination ++++++++++++ + +Eliminating a disjunction requires a *motive*, a goal proposition to +be derived from a proof of ``sum p q`` or ``p \/ q``. + +In constructive style, eliminating ``sum p q`` amounts to just +pattern matching on the cases and constructing a proof of the goal +by applying a suitable goal-producing hypothesis. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: sum_elim$ + :end-before: //SNIPPET_END: sum_elim$ + +The squashed version is similar, except the case analysis can either +be automated by SMT or explicitly handled using the syntactic +sugar. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: or_elim$ + :end-before: //SNIPPET_END: or_elim$ + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: or_elim_sugar$ + :end-before: //SNIPPET_END: or_elim_sugar$ + +Implication +........... + +One of the elimination principles for disjunction used the implication +connective ``==>``. Its definition is shown below: + +.. code-block:: fstar + + let ( ==> ) (p q : Type) = squash (p -> q) + +That is, ``==>`` is just the squashed version of the non-dependent +arrow type ``->``. + +.. note:: + + In ``Prims``, the definition of ``p ==> q`` is actually ``squash (p + -> GTot q)``, a **ghost** function from ``p`` to ``q``. We'll learn + about this more when we encounter effects. + +Introduction +++++++++++++ + +Introducing a constructive arrow ``p -> q`` just involves constructing +a :math:`\lambda`-literal of the appropriate type. + +One can turn several kinds of arrows into implications, as shown below. + +One option is to directly use a function from the ``FStar.Classical`` +library, as shown below: + +.. code-block:: fstar + + val impl_intro_tot (#p #q: Type) (f: (p -> q)) : (p ==> q) + +However, this form is seldom used in F*. Instead, one often works with +functions between squashed propositions, or Lemmas, turning them into +implications when needed. We show a few styles below. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: implies_intro$ + :end-before: //SNIPPET_END: implies_intro$ + +Unlike the other connectives, there is no fully automated SMT-enabled +way to turn an arrow type into an implication. Of course, the form +shown above remains just sugar: it may be instructive to look at its +desugaring, shown below. + +.. code-block:: fstar + + let implies_intro_1 (#p #q:Type) (pq: (squash p -> squash q)) + : squash (p ==> q) + = FStar.Classical.Sugar.implies_intro + p + (fun (_: squash p) -> q) + (fun (pf_p: squash p) -> pq pf_p) + +``FStar.Squash`` and ``FStar.Classical`` provide the basic building +blocks and the sugar packages it into a more convenient form for use. + +Elimination ++++++++++++ + +Of course, the elimination form for a constructive implication, i.e., +``p -> q`` is just function application. + +.. code-block:: fstar + + let arrow_elim #p #q (f:p -> q) (x:p) : q = f x + +The elimination rule for the squashed form is the classical logical +rule *modus ponens*, which is usually very well automated by SMT, as +shown in ``implies_elim`` below. We also provide syntactic sugar for +it, for completeness, though it is seldom used in practice. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: implies_elim$ + :end-before: //SNIPPET_END: implies_elim$ + +Negation +........ + +Negation is just a special case of implication. + +In its constructive form, it corresponds to ``p -> empty``. + +In ``Prims``, we define ``~p`` as ``p ==> False``. + +Being just an abbreviation for an implication to ``False``, negation +has no particular introduction or elimination forms of its +own. However, the following forms are easily derivable. + +Introduction (Exercise) ++++++++++++++++++++++++ + +Prove the following introduction rule for negation: + +`Exercise file <../code/exercises/Part2.Connectives.Negation.fst>`__ + +.. code-block:: fstar + + val neg_intro #p (f:squash p -> squash False) + : squash (~p) + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: neg_intro$ + :end-before: //SNIPPET_END: neg_intro$ + +-------------------------------------------------------------------------------- + + +Elimination (Exercise) ++++++++++++++++++++++++ + +Prove the following elimination rule for negation using the sugar +rather than just SMT only. + +.. code-block:: fstar + + val neg_elim #p #q (f:squash (~p)) (x:unit -> Lemma p) + : squash (~q) + +`Exercise file <../code/exercises/Part2.Connectives.Negation.fst>`__ + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: neg_elim$ + :end-before: //SNIPPET_END: neg_elim$ + +-------------------------------------------------------------------------------- + +Universal Quantification +........................ + +Whereas implication is represented by the non-dependent arrow ``p -> +q``, universal quantification corresponds to the dependent arrow ``x:t +-> q x``. Its classical form in ``forall (x:t). q x``, and is defined +in as shown below: + +.. code-block:: fstar + + let ( forall ) #t (q:t -> Type) = squash (x:t -> q x) + +.. note:: + + As with ``==>``, in ``Prims`` uses ``x:t -> GTot (q x)``, a ghost + arrow, though the difference is not yet significant. + +Introduction +++++++++++++ + +Introducing a dependent function type ``x:t -> p x`` is just like +introducing a non-dependent one: use a lambda literal. + +For the squashed form, F* provides sugar for use with several styles, +where names corresponding to each of the ``forall``-bound variables on +the ``introduce`` line are in scope for the proof term on the ``with`` +line. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: forall_intro$ + :end-before: //SNIPPET_END: forall_intro$ + +Note, as ``forall_intro_3`` shows, the sugar also works for ``forall`` +quantifiers of arities greater than 1. + +Elimination ++++++++++++ + +Eliminating a dependent function corresponds to dependent function +application. + +.. code-block:: fstar + + let dep_arrow_elim #t #q (f:(x:t -> q x)) (x:t) : q x = f x + +For the squashed version, eliminating a ``forall`` quantifier amounts +to instantiating the quantifier for a given term. Automating proofs +that require quantifier instantiation is a large topic in its own +right, as we'll cover in a later section---this `wiki page +`_ +provides some hints. + +Often, eliminating a universal quantifier is automated by the SMT +solver, as shown below, where the SMT solver easily instantiates the +quantified hypothesis ``f`` with ``a``. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: forall_elim_1$ + :end-before: //SNIPPET_END: forall_elim_1$ + +But, F* also provides syntactic sugar to explicitly trigger quantifier +insantiation (as shown below), where the terms provided on the +``with`` line are instantiations for each of the binders on the +``eliminate`` line. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: forall_elim_sugar$ + :end-before: //SNIPPET_END: forall_elim_sugar$ + +Its desugaring may be illuminating: + +.. code-block:: fstar + + let forall_elim_2 (f: squash (forall (x0: t0) (x1: t1 x0). q x0 x1)) + (v0: t0) + (v1: t1 v0) + : squash (q v0 v1) + = FStar.Classical.Sugar.forall_elim + #(t1 v0) + #(fun x1 -> q v0 x1) + v1 + (FStar.Classical.Sugar.forall_elim + #t0 + #(fun x0 -> forall (x1: t1 x0). q x0 x1) + v0 + ()) + +.. _Part2_connectives_exists: + +Existential Quantification +.......................... + +Finally, we come to existential quantification. Its constructive form +is a dependent pair, a dependent version of the pair used to represent +conjunctions. The following inductive type is defined in ``Prims``. + +.. code-block:: fstar + + type dtuple2 (a:Type) (b: a -> Type) = + | Mkdtuple2 : x:a -> y:b x -> dtuple2 a b + +As with ``tuple2``, F* offers specialized syntax for ``dtuple2``: + + * Instead of ``dtuple2 a (fun (x:a) -> b x)``, one writes ``x:a & b x``. + + * Instead of writing ``Mkdtuple2 x y``, one writes ``(| x, y |)``. + +The existential quantifier ``exists (x:t). p x`` is a squashed version +of the dependent pair: + +.. code-block:: fstar + + let ( exists ) (#a:Type) (#b:a -> Type) = squash (x:a & b x) + +Introduction +++++++++++++ + +Introducing a constructive proof of ``x:a & b x`` is just a question +of using the constructor---we show a concrete instance below. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: dtuple2_intro$ + :end-before: //SNIPPET_END: dtuple2_intro$ + +For the squashed version, introducing an ``exists (x:t). p x`` +automatically using the SMT solver requires finding an instance ``a`` +for the quantifier such that ``p a`` is derivable---this is the dual +problem of quantifier instantiation mentioned with universal + +In the first example below, the SMT solver finds the instantiation and +proof automatically, while in the latter two, the user picks which +instantiation and proof to provide. + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: exists_intro$ + :end-before: //SNIPPET_END: exists_intro$ + +Elimination ++++++++++++ + +Just as with disjunction and conjunction, eliminating ``dtuple2`` or +``exists`` requires a motive, a goal proposition that *does not +mention* the bound variable of the quantifier. + +For constructive proofs, this is just a pattern match: + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: dtuple2_elim$ + :end-before: //SNIPPET_END: dtuple2_elim$ + +For the ``exists``, the following sugar provides an elimination +principle: + +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: exists_elim$ + :end-before: //SNIPPET_END: exists_elim$ + +Names corresponding to the binders on the ``eliminate`` line are in +scope in the ``with`` line, which additionally binds a name for a +proof term corresponding to the body of the existential formula. That +is, in the examples above, ``x:t`` is implicitly in scope for the proof +term, while ``pf_p: squash p``. + +Exercise +++++++++ + +In a :ref:`previous exercise `, we defined a +function to insert an element in a Merkle tree and had it return a new +root hash and an updated Merkle tree. Our solution had the following +signature: + +.. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: update_hint + :end-before: //SNIPPET_END: update_hint + +Revise the solution so that it instead returns a dependent +pair. ``dtuple2`` is already defined in ``Prims``, so you don't have +to define it again. + +`Exercise file <../code/exercises/Part2.MerkleTreeUpdate.fst>`__ + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: update + :end-before: //SNIPPET_END: update diff --git a/book/part2/part2_merkle.rst b/book/part2/part2_merkle.rst new file mode 100644 index 00000000000..1ac74c9c0c1 --- /dev/null +++ b/book/part2/part2_merkle.rst @@ -0,0 +1,385 @@ +.. _Part2_merkle: + +Merkle Trees +============ + +A `Merkle tree `_ is a +cryptographic data structure designed by `Ralph Merkle +`_ in the late 1970s and +has grown dramatically in prominence in the last few years, inasmuch +as variants of Merkle trees are at the core of most `blockchain +systems `_. + +A Merkle tree makes use of cryptographic hashes to enable efficient +cryptographic proofs of the authenticity of data stored in the +tree. In particular, for a Merkle tree containing :math:`2^n` data +items, it only takes :math:`n` hash computations to prove that a +particular item is in the tree. + +In this section, we build a very simple, but canonical, Merkle tree +and prove it correct and cryptographically secure. And we'll use +several indexed inductive types to do it. Thanks to Aseem Rastogi for +this example! + +Setting +....... + +Merkle trees have many applications. To motivate our presentation +here, consider the following simple scenario. + +A content provider (someone like, say, the New York Times) has a large +archive of digital artifacts---documents, multimedia files, etc. These +artifacts are circulated among users, but when receiving an artifact +one may question its authenticity. One way to ensure the authenticity +of received artifacts is for the content provider to use a digital +signature based on a public-key cryptosystem and for users to verify +these signatures upon receiving an artifact. However, signatures can +be quite heavyweight for certain applications. + +Instead, the content provider can organize their archive into a Merkle +tree, a tree of hashes with the artifacts themselves stored at the +leaves, such that a single hash associated with the root node of the +tree authenticates *all* the artifacts in the tree. By publishing just +this root hash, and associating with each artifact a path in the tree +from the root to it, a skeptical client can quickly check using a +small number of hash computations (logarithmic in the size of the +entire archive) whether or not a given artifact is authentic (by +recomputing the root hash and checking if it matches the known +published root hash). + + +Intuitions +.......... + +Our Merkle tree will be a full binary tree of height :math:`n` storing +:math:`2^n` data items and their corresponding hashes at the +nodes. The main idea of a Merkle tree is for each internal node to +also maintain a *hash of the hashes* stored at each of its +children. If the hash algorithm being used is cryptographically +secure, in the sense that it is collision resistant (i.e., it is +computationally hard to find two strings that hash to the same value), +then the hash associated with the root node authenticates the content +of the entire tree. + +Informally, a Merkle tree is an authenticated data structure in that +it is computationally hard to tamper with any of the data items in the +tree while still producing the same root hash. Further, to prove that +a particular data item ``d`` is in the tree, it suffices to provide +the hashes associated with the nodes in the path from the root to that +the leaf containing that item ``d``, and one can easily check by +comparing hashes that the claimed path is accurate. In fact, we can +prove that if a claimed path through the tree attests to the presence +of some other item ``d' <> d``, then we can construct a collision on +the underlying hash algorithm---this property wil be our main proof of +security. + + +Preliminaries +............. + +We'll model the resources and the hashes we store in our tree as +strings of characters. F* standard library ``FStar.String`` provides +some utilities to work with strings. + +In the code listing below, we define the following + + * ``lstring n``, the type of strings of length ``n``. Like the + ``vec`` type, ``lstring`` is a length-indexed type; unlike + ``vector`` it is defined using a refinement type rather than an + indexed inductive type. Defining indexed types using refinements + is quite common in F*. + + * ``concat``, a utility to concatenate strings, with its type + proving that the resulting string's length is the sum of the lengths + of the input strings. + + * ``hash_size`` and ``hash``, a parameter of our development + describing the length in characters of a ``hash`` function. The F* + keyword ``assume`` allows you to assume the existence of a symbol + at a given type. Use it with care, since you can trivially prove + anything by including an ``assume nonsense : False``. + + * The type of resources we store in the tree will just be + ``resource``, an alias for ``string``. + +.. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: preliminaries + :end-before: //SNIPPET_END: preliminaries + + +Defining the Merkle tree +........................ + +The inductive type ``mtree`` below defines our Merkle tree. The type +has *two* indices, such that ``mtree n h`` is the type of a Merkle +tree of height ``n`` whose root node is associated with the hash +``h``. + +Leaves are trees of height ``0`` and are constructed using ``L res``, +where the hash associated with this node is just ``hash res``, the +hash of the resource stored at the leaf. + +Internal nodes of the tree are constructed using ``N left right``, +where both the ``left`` and ``right`` trees have the same height +``n``, producing a tree of height ``n + 1``. More interestingly, the +hash associated with ``N left right`` is ``hash (concat hl hr)``, the +hash of the concatenation of hashes of the left and right subtrees. + + +.. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: mtree + :end-before: //SNIPPET_END: mtree + +In our previous examples like vectors, the index of the type +abstracts, or summarizes, some property of the type, e.g., the +length. This is also the case with ``mtree``, where the first index is +an abstraction summarizing only the height of the tree; the second +index, being a cryptographic hash, summarizes the entire contents of +the tree. + + +Accessing an element in the tree +................................ + +A resource identifier ``resource_id`` is a path in the tree from the +root to the leaf storing that resource. A path is just a list of +booleans describing whether to descend left or right from a node. + +Just like a regular binary tree, it's easy to access an element in the +tree by specifying its ``resource_id``. + + +Exercise +^^^^^^^^ + +Implement a function to access an element in a ``mtree`` in given a +``rid : list bool``. Figuring out its type, including its decreases +clause, is the most interesting part. The function itself is +straightforward. + +`Exercise file <../code/exercises/Part2.MerkleTreeGet.fst>`__ + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: get + :end-before: //SNIPPET_END: get + +-------------------------------------------------------------------------------- + +The Prover +.......... + +Unlike the ordinary ``get`` function, we can define a function +``get_with_evidence`` that retrieves a resource from the tree together +with some evidence that that resource really is present in the tree. +The evidence contains the resource identifier and the hashes of +sibling nodes along the path from root to that item. + +First, we define ``resource_with_evidence n``, an indexed type that +packages a ``res:resource`` with its ``rid:resource_id`` and +``hashes:list hash_t``---both ``rid`` and ``hashes`` have the same +length, which is the index of the constructed type. + +The function ``get_with_evidence`` is similar to ``get``, except as it +returns from descending into a child node, it adds the hash of the +other child node to the list of hashes. + +.. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: prover + :end-before: //SNIPPET_END: prover + +In the cryptographic literature, this function is sometimes calles +*the prover*. A ``RES r ri hs`` is a claimed proof of the membership +of ``r`` in the tree at the location specified by ``ri``. + +Going back to our motivating scenario, artifacts distributed by our +content provider would be elements of the type +``resource_with_evidence n``, enabling clients to verify that a given +artifact is authentic, as shown next. + +The Verifier +............ + +Our next step is to build a checker of claimed proofs, sometimes +called *a verifier*. The function ``verify`` below takes a +``p:resource_with_evidence n``, re-computes the root hash from the +evidence presented, and checks that that hash matches the root hash of +a given Merkle tree. Note, the ``tree`` itself is irrelevant: all +that's needed to verify the evidence is *the root hash* of the Merkle +tree. + +.. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: verify + :end-before: //SNIPPET_END: verify + +The main work is done by ``compute_root_hash``, shown below. + + * In the first branch, we simply hash the resource itself. + + * In the second branch, we recompute the hash from the tail of the + path, and then based on which direction was taken, we either + concatenate sibling hash on the left or the right, and hash the + result. + +.. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: compute_root_hash + :end-before: //SNIPPET_END: compute_root_hash + + +Convince yourself of why this is type-correct---refer back to the +description of :ref:`vectors`, if needed. For example, +why is it safe to call ``L.hd`` to access the first element of +``hashes``? + + +Correctness +........... + +Now, we can prove our main correctness theorem, namely that +``get_with_evidence`` returns a resource with verifiable +evidence. + +.. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: correctness + :end-before: //SNIPPET_END: correctness + +The proof is a simple proof by induction on the height of the tree, or +equivalently, the length of the resource id. + +In other words, evidence constructed by a honest prover is accepted by +our verifier. + +Security +........ + +The main security theorem associated with this construction is the +following: if the verifier can be convinced to accept a resource with +evidence of the form ``RES r rid hs``, and if the resource in the +Merkle tree associated with ``rid`` is *not* ``r``, then we can easily +construct a collision on the underlying cryptographic hash. Since the +hash is meant to be collision resistant, one should conclude that it +is at least as hard to convince our verifier to accept incorrect +evidence as it is to find collisions on the underlying hash. + +We start by defining the type of a ``hash_collision``, a pair of +distinct strings that hash to the same value. + +.. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: hash_collision + :end-before: //SNIPPET_END: hash_collision + +The ``security`` theorem shown below takes a ``tree`` and +``p:resource_with_evidence n``, where the refinement on ``p`` states +that the verifier accepts the evidence (``verify p tree``) although +the resource associated with ``p.ri`` is not ``p.res``: in this case, +we can build a function, by induction on the height of the tree, that +returns a hash collision. + +.. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: security + :end-before: //SNIPPET_END: security + +We look at its cases in detail: + + * In the base case, it's easy to construct a hash collision + directly from the differing resources. + + * Otherwise, we recompute the hash associate with the current node + from the tail of the evidence presented, and the two cases of the + left and right subtrees are symmetric. + + - If the recomputed hash matches the hash of the node, then we + can generate a collision just by the induction hypothesis on + the left or right subtree. + + - Otherwise, we can build a hash collision, relying on + ``String.concat_injective``, a lemma from the library stating + that the concatenation of two pairs of equal length strings are + equal only if their components are. Knowing that ``h' <> h1`` + (or, symmetically, ``h' <> h2``) this allows us to prove that + the concatenations are unequal, although their hashes are, by + assumption, equal. + +.. _Part2_merkle_insert: + +Exercise +........ + +Implement a function to update an ``mtree`` at a given +``rid:resource_id`` with a new resource ``res:resource``. The +resulting tree will have a new root hash, so you will have to return +the new hash along with the updated tree. + +`Exercise file <../code/exercises/Part2.MerkleTreeUpdate_V0.fst>`__ + +.. container:: toggle + + .. container:: header + + **Hint** + + One type of the ``update`` function could be as follows: + + .. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: update_hint + :end-before: //SNIPPET_END: update_hint + +-------------------------------------------------------------------------------- + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/MerkleTree.fst + :language: fstar + :start-after: //SNIPPET_START: update_mtree' + :end-before: //SNIPPET_END: update_mtree' + + One interesting part of our solution is that we never explicitly + construct the hash of the nodes. Instead, we just use ``_`` and + let F* infer the calls to the hash functions. + +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +Summary and Further Reading +........................... + +In summary, we've built a simple but powerful authenticated data +structure with a proof of its correctness and cryptographic security. + +In practice, Merkle trees can be much more sophisticated than our the +most basic one shown here. For instance, they can support incremental +updates, contain optimizations for different kinds of workloads, +including sparse trees, and be implemented using high-performance, +mutable structures. + +You can read more about various flavors of Merkle trees implemented in +F* in the following papers. + +* `EverCrypt, Section VII (B) + `_, + describes a high-performance Merkle tree with fast incremental + updates. + +* `FastVer `_ + describes the design and use of hybrid authenticated data strutures, + including sparse Merkle trees, for applications such as verificable + key-value stores. diff --git a/book/part2/part2_par.rst b/book/part2/part2_par.rst new file mode 100644 index 00000000000..fd2d5933b2d --- /dev/null +++ b/book/part2/part2_par.rst @@ -0,0 +1,686 @@ +.. _Part2_par: + +A First Model of Computational Effects +====================================== + +As a final chapter in this section, we show how inductive types can be +used model not just data, but also *computations*, including +computations with side effects, like mutable state and shared-memory +concurrency. This is meant to also give a taste of the next section in +this book, which deals with modeling and proving properties of +programs with effects and F*'s user-extensible system of indexed +effects. + +Thanks to Guido Martinez and Danel Ahman, for some of the content in +this chapter. + +A First Taste: The State Monad +++++++++++++++++++++++++++++++ + +All the programs we've written so far have been purely +functional. However, one can model programs that manipulate mutable +state within a purely functional language, and one common but powerful +way to do this is with something called a *monad*, an idea that was +introduced to functional programmers in the late 1980s and early 90s +by `Philip Wadler `_ +building on semantic foundations developed by `Eugenio Moggi +`_. If you've been +puzzled about monads before, we'll start from scratch here, and +hopefully this time it will all make sense! + +Consider modeling a program that manipulates a single piece of mutable +state, just a single integer that programs can read and write, and +which returns a result of type ``a``. One way to do this is to +represent such a *stateful computation* as a program whose type is +``st a``: + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: st$ + :end-before: //SNIPPET_END: st$ + +A ``(st a)`` computation is a function which when given an initial +value for the state ``s0`` returns a pair ``(x, s1)`` with the result +of the computation ``x:a`` and a final value for the state ``s1``. + +For example, a computation that reads the state, increments it, and +returns the initial value of the state, can be expressed as shown +below. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: read_and_increment_v0$ + :end-before: //SNIPPET_END: read_and_increment_v0$ + +This is pretty straightforward, but writing computations in this style +can be quite tedious and error prone. For example, if you wanted to +read the state and increment it twice, one would write: + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: inc_twice_v0$ + :end-before: //SNIPPET_END: inc_twice_v0$ + +This is quite clumsy, since at each call to ``read_and_increment_v0`` +we had to be careful to pass it the "the most recent version" of the +state. For instance, a small typo could easily have caused us to write +the program below, where we pass ``s0`` to the second call of +``read_and_increment``, causing the program to only increment the +state once. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: inc_twice_buggy$ + :end-before: //SNIPPET_END: inc_twice_buggy$ + +The main idea with the state monad is to structure stateful programs +by abstracting out all the plumbing related to manipulating the state, +eliminating some of the tedium and possibilities for errors. + +The way this works is by defining a functions to read and write the +state, plus a couple of functions to return a pure value without +reading or writing the state (a kind of an identity function that's a +noop on the state); and a function to sequentially compose a pair of +stateful computations. + +* The function ``read : st int`` below, reads the state and returns it, + without modifying the state. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: read$ + :end-before: //SNIPPET_END: read$ + +* The function ``write (s1:int) : st unit`` below, sets the state to ``s1`` and + returns ``() : unit``. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: write$ + :end-before: //SNIPPET_END: write$ + +* The function ``bind`` is perhaps the most interesting. Given a + stateful computation ``f: st a`` and another computation ``g : a -> + st b`` which depends on the result of ``f`` and then may read or + write the state returning a ``b``; ``bind f g`` composes ``f`` and + ``g`` sequentially, passing the initial state ``s0`` to ``f``, then + passing its result ``x`` and next state ``s1`` to ``g``. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: bind$ + :end-before: //SNIPPET_END: bind$ + +* Finally, ``return`` promotes a pure value ``x:a`` into an ``st a``, + without touching the state. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: return$ + :end-before: //SNIPPET_END: return$ + +Some stateful programs +---------------------- + +With these combinators in hand, we can write stateful programs in a +more compact style, never directly manipulating the underlying integer +variable that holds the state directly. + +Here's a next attempt at ``read_and_increment``: + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: read_and_increment_v1$ + :end-before: //SNIPPET_END: read_and_increment_v1$ + +Now, you're probably thinking that this version is even worse than +``read_and_increment_v0``! But, the program looks obscure "just" +because it's using a convoluted syntax to call ``bind``. Many +languages, most famously Haskell, provide specialized syntax to +simplify writing computations that work with APIs like ``bind`` and +``return``. F* provides some syntactic sugar to handle this too. + +Monadic let bindings +++++++++++++++++++++ + +The definition below defines a function with a special name +``let!``. Names of this form, the token ``let`` followed by a sequence +of one or more operator characters such as ``!``, ``?``, ``@``, ``$``, +``<``, and ``>`` are monadic let-binding operators. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: let!$ + :end-before: //SNIPPET_END: let!$ + +With ``let!`` in scope, the following syntactic sugar becomes available: + +* Instead of writing ``bind f (fun x -> e)`` you can write ``let! x = f in e``. + +* Instead of writing ``bind f (fun _ -> e)`` you can write ``f ;! + e``, i.e., a semicolon followed the sequence of operator characters + uses in the monadic let-binding operator. + +* Instead of writing ``bind f (fun x -> match x with ...)``, you can + write ``match! f with ...`` + +* Instead of writing ``bind f (fun x -> if x then ...)``, you can + write ``if! f then ...`` + +See this file `MonadicLetBindings.fst +`_ +for more details an examples of the syntactic sugar. + +Using this syntactic sugar, we come to our final version of +``read_and_increment``, where now, hopefully, the imperative-looking +state updates make the intent of our program clear. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: read_and_increment$ + :end-before: //SNIPPET_END: read_and_increment$ + +Having structured our programs with ``return`` and ``bind``, larger +``st`` computations can be built from smaller ones, without having to +worry about how to plumb the state through---that's handled once and +for all by our combinators. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: inc_twice$ + :end-before: //SNIPPET_END: inc_twice$ + +.. _Part2_monad_intro: + +``st`` is a monad +----------------- + +It turns out that every API that is structured like our ``st`` is an +instance of a general pattern called a *monad*, an algebraic +structure. Specifically, a monad consists of: + + * A type operator ``m : Type -> Type`` + * A function ``return (#a:Type) (x:a) : m a`` + * A function ``bind (#a #b:Type) (f:m a) (g: a -> m b) : m b`` + +which satisfy the following laws, where `~` is some suitable +equivalence relation on ``m a`` + + * Left identity: ``bind (return x) f ~ f`` + * Right identity: ``bind f return ~ f`` + * Associativity: ``bind f1 (fun x -> bind (f2 x) f3) ~ bind (bind f1 f2) g`` + +Its easy to prove that ``st``, ``return``, and ``bind`` satisfy these +laws in F*, where we pick the equivalence relation to equate functions +that take equal arguments to equal results. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: monad_laws$ + :end-before: //SNIPPET_END: monad_laws$ + +These laws are practically useful in that they can catch bugs in our +implementations of the combinators. For instance, suppose we were to +write ``bind_buggy`` below, which like in ``inc_twice_buggy``, +mistakenly re-uses the old state ``s0`` when calling ``g``---in this +case, the ``right_identity`` law below cannot be proved. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: bind_buggy$ + :end-before: //SNIPPET_END: bind_buggy$ + +We can also prove laws about how the stateful actions, ``read`` and +``write``, interact with each other in sequential composition. + +.. literalinclude:: ../code/Part2.STInt.fst + :language: fstar + :start-after: //SNIPPET_START: action_laws$ + :end-before: //SNIPPET_END: action_laws$ + +That completes our tour of our very first monad, the state monad. + +Exercise +-------- + +Make the ``st`` type generic, so that instead of the state being fixed +to a single integer value, it can be used with any type for the +state. I.e., define ``st (s:Type) (a:Type) : Type``, where ``s`` is +the type of the state. + +Adapt the full development seen above to work with ``st s``, including +proving the various laws. + +`Exercise file <../code/exercises/Part2.ST.fst>`__ + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.ST.fst + :language: fstar + +-------------------------------------------------------------------------------- + +Exercise +-------- + +Monads can be used to model many computational effects, not just +mutable state. Another common example is to use monads to model +computations that may raise runtime errors. Here's an exercise to help +you see how. + +Prove that the ``option`` type can be made into a monad, i.e., define +``bind`` and ``return`` and prove the monad laws. + +`Exercise file <../code/exercises/Part2.Option.fst>`__ + +-------------------------------------------------------------------------------- + +Computation Trees, or Monads Generically +++++++++++++++++++++++++++++++++++++++++ + +Each time one defines a monad to model a computational effect, one +usually thinks first of the effectful *actions* involved (e.g., +reading and writing the state, or raising an error), and then finds a +way to pakage those actions into the interface of monad with +``return`` and ``bind``, and then, to keep things honest, proves that +the implementation satisfies the monad laws. + +However, a lot of this is boilerplate and can be done once and for all +by representing effectful computations not just as functions (as we +did with ``st a = int -> a & s``) but instead as an inductive type +that models a *computation tree*, with effectful actions made explicit +at each node in the tree. One can prove that this representation, +sometimes called a *free monad*, is a monad, and then instantiate it +repeatedly for the particular kinds of actions that one may want to +use in given program. + +.. note :: + + In this section, we're scratching the surface of a rich area of + research called *algebraic effects*. While what we show here is not + a full-blown algebraic effects development (we'll save that for a + later chapter), here are some other resources about it. + + * `Alg.fst + `_: + An F* development with algebraic effects (to be covered in + detail later). + + * `Koka `_, a + language with algebraic effects at its core + + * A bibliography about `effects + `_ + +We'll start our development of computation trees with a type +describing the signature of a language of actions. + +.. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: action_class$ + :end-before: //SNIPPET_END: action_class$ + +This kind of signature is sometimes called a *type class*, a type +``act:Type``, together with some operations it supports. In this case, +the operations tell us what kind of input and output a given action +expects. + +.. note:: + + F* also provides support for type classes and inference of type + class instantiations. This will be described in a later + chapter. Meanwhile, you can learn more about type classes in F* + `from the wiki + `_ + and from these `examples + `_. + +For example, if we were interested in just the read/write actions on a +mutable integer state (as in our ``st a`` example), we could build an +instance of the ``action_class``, as shown below. + +.. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: rw_action_class$ + :end-before: //SNIPPET_END: rw_action_class$ + +However, we can define a type ``tree acts a``, the type of a computation +tree whose effectful actions are from the class ``acts``, completely +generically in the actions themselves. + +.. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: tree$ + :end-before: //SNIPPET_END: tree$ + +A ``tree act a`` has just two cases: + + * Either it is a leaf node, ``Return x``, modeling a computation + that immediately returns the value ``x``; + + * Or, we have a node ``DoThen act input k``, modeling a computation + that begins with some action ``act``, to which we pass some input, + and where ``k`` represents all the possible "continuations" of the + action, represented by a ``tree act a`` for each possible output + returned by the action. That is, ``DoThen`` represents a node in + the tree with a single action and a possibly infinite number of + sub-trees. + +With this representation we can define ``return`` and ``bind``, and +prove the monad laws once and for all. + +.. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: return and bind$ + :end-before: //SNIPPET_END: return and bind$ + +* The ``return`` combinator is easy, since we already have a + ``Return`` leaf node in the tree type. + +* The ``bind`` combinator is a little more interesting, involving a + structural recursion over the tree, relying (as we did in the + previous chapter on well-founded recursion) on the property that all + the trees returned by ``k`` are strictly smaller than the original + tree ``f``. + +To prove the monad laws, we first need to define an equivalence +relation on trees---this relation is not quite just ``==``, since each +continuation in the tree is function which itself returns a tree. So, +we define ``equiv`` blow, relating trees that are both ``Returns``, or +when they both begin with the same action and have +pointwise-equivalent continuations. + +.. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: equiv$ + :end-before: //SNIPPET_END: equiv$ + +.. note:: + + We are specifically avoiding the use of :ref:`functional + extensionality ` here, a property which allows + equating pointwise equal :math:`\eta`-expanded functions. We show + how one can use functional extensionality in this development as an + advanced exercise. + +To prove that ``equiv`` is an equivalence relation, here are lemmas +that prove that it is reflexive, symmetric, and transitive---we see +here a use of the syntactic sugar for logical connectives, +:ref:`introduced in a previous chapter `. + +.. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: equiv is an equivalence$ + :end-before: //SNIPPET_END: equiv is an equivalence$ + +Now, we can prove that ``tree`` satisifies the monad laws with respect +to ``equiv``. + +.. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: tree is a monad$ + :end-before: //SNIPPET_END: tree is a monad$ + +The associativity law, in particular, should make intuitive sense in +that a ``tree acts a`` represents a computation in a canonical fully +left-associative form, i.e., a single action followed by its +continuation. As such, no matter how you associate computations in +``bind``, the underlying representation is always fully +left-associated. + +Having defined our computation trees generically, we can use them with +any actions we like. For example, here's our ``read_and_increment`` +re-built using computation trees. + +.. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: read_and_increment$ + :end-before: //SNIPPET_END: read_and_increment$ + +Finally, given a computation tree we can "run" it, by interpreting it +as a state-passing function. + +.. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: interp$ + :end-before: //SNIPPET_END: interp$ + +.. note:: + + A main difference between what we've shown here with ``interp`` and + a general treament of algebraic effects is that rather than + "bake-in" the interpretation of the individual actions in + ``interp``, we can also abstract the semantics of the actions using + an idea similar to exception handling, allowing the context to + customize the semantics of the actions simply by providing a + different handler. + +Exercise +-------- + +Prove that the ``interp`` function interprets equivalent trees ``f`` +and ``g`` to pointwise equivalent functions. + +`Exercise File <../code/exercises/Part2.ComputationTreeEquiv.fst>`__ + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.Free.fst + :language: fstar + :start-after: //SNIPPET_START: interp_equiv$ + :end-before: //SNIPPET_END: interp_equiv$ + +-------------------------------------------------------------------------------- + +Exercise +-------- + +Instead of proving every time that a function like ``interp`` produces +equivalent results when applied to equivalent trees, using functional +extensionality, we can prove that equivalent trees are actually +provably equal, i.e., ``equiv x y ==> x == y``. + +This is a little technical, since although functional extensionality +is a theorem in F*, it is only true of :math:`\eta`-expanded functions. + +Try to use ``FStar.FunctionalExtensionality.fsti`` to adapt the +definitions shown above so that we can prove the lemma ``equiv x y ==> +x == y``. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.FreeFunExt.fst + :language: fstar + +-------------------------------------------------------------------------------- + +Manipulating Computation Trees: Nondeterminism and Concurrency +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +As a final bit, we show that representing computations as trees is not +just useful from a perspective of genericity and code re-use. +Computation trees expose the structure of a computation in a way that +allows us to manipulate it, e.g., interpreting actions in an +alternative semantics. + +In this section, we enhance our computation trees to support +non-deterministic choice, i.e., given pair of computations ``l, r:tree +acts a``, we can non-deterministically choose to evaluate ``l`` or +``r``. With this capability, we can also express some models of +concurrency, e.g., a semantics that interleaves imperative actions +from several threads. + +Let's start by enhancing our ``tree`` type to now include an node ``Or +l r``, to represent non-deterministic choice between ``l`` and ``r``. + +.. literalinclude:: ../code/Part2.Par.fst + :language: fstar + :start-after: //SNIPPET_START: tree$ + :end-before: //SNIPPET_END: tree$ + +As before, we can define ``return`` and ``bind``, this time in +``bind`` we sequence ``g`` after both choices in ``Or``. + +.. literalinclude:: ../code/Part2.Par.fst + :language: fstar + :start-after: //SNIPPET_START: return and bind$ + :end-before: //SNIPPET_END: return and bind$ + +What's more interesting is that, in addition to sequential +composition, we can also define parallel composition of a pair of +computations using ``par f g``, as shown below. + +.. literalinclude:: ../code/Part2.Par.fst + :language: fstar + :start-after: //SNIPPET_START: par$ + :end-before: //SNIPPET_END: par$ + +There's quite a lot going on here, so let's break it down a bit: + + * The functions ``l_par f g`` and ``r_par f g`` are mutually + recursive and define an interleaving semantics of the actions in + ``f`` and ``g``. + + * ``l_par f g`` is left-biased: picking an action from ``f`` to + execute first (if any are left); while ``r_par f g`` is + right-biased, picking an action from ``g`` to execute first. + + * Consider the ``DoThen`` case in ``l_par``: if picks the head + action ``a`` from ``f`` and the recurses in the continuation with + ``r_par (k x) g``, to prefer executing first an action from ``g`` + rather than ``k x``. The ``DoThen`` case of ``r_par`` is + symmetric. + + * For ``l_par``, in the non-deterministic choice case (``Or``), we + interleave either choice of ``f`` with ``g``, and ``r_par`` is + symmetric. + + * Finally, we define parallel composition ``par f g`` as the + non-deterministic choice of either the left-biased or right-biased + interleaving of the actions of ``f`` and ``g``. This fixes the + semantics of parallel composition to a round-robin scheduling of + the actions between the threads, but one could imagine + implementing other kinds of schedulers too. + +As before, we can now instantiate our tree with read/write actions and +write some simple programs, including ``par_inc``, a computation that +tries to increment the counter twice in parallel. + +.. literalinclude:: ../code/Part2.Par.fst + :language: fstar + :start-after: //SNIPPET_START: sample program$ + :end-before: //SNIPPET_END: sample program$ + +However, there's trouble ahead---because of the interleaving +semantics, we don't actually increment the state twice. + +To check, let's define an interpretation function to run our +computations. Since we need to resolve the non-deterministic choice in +the ``Or`` nodes, we'll parameterize our intepreter by a source of +"randomness", an infinite stream of booleans. + +.. literalinclude:: ../code/Part2.Par.fst + :language: fstar + :start-after: //SNIPPET_START: interp$ + :end-before: //SNIPPET_END: interp$ + +This interpreter is very similar to our prior interpreter, except in +the ``Or`` case, we read a boolean from ``rand``, our randomness +stream, and choose the left- or right-branch accordingly. + +We can run our program on this interpreter and check what it +returns. One way to do this is to make use of F*'s normalizer, the +abstract machine that F* uses to reduce computations during +type-checking. The ``assert_norm p`` feature used below instructs F* +to symbolically reduce the term ``p`` as much as possible and then +check that the result is equivalent to ``True``. + +.. note:: + + F*'s emacs mode ``fstar-mode.el`` provides some utilites to allow + reducing terms of F*'s abstract machine and showing the results to + the user. F*'s tactics also also allow evaluating terms and viewing + the results---we leave further discussion of these features to a + future chapter. + + +.. literalinclude:: ../code/Part2.Par.fst + :language: fstar + :start-after: //SNIPPET_START: test_par_inc$ + :end-before: //SNIPPET_END: test_par_inc$ + +In this case, we ask F* to interpret ``par_inc`` on the interpreter we +just defined. And, indeed, F* confirms that in the final state, we +have incremented the state only once. Due to the round-robin +scheduling of actions, the interpreter has executed both the reads +before both the writes, making one of the reads and one of the writes +redundant. + +Exercise +-------- + +Define an action class that include an increment operation, in +addition to reads and writes. Adapt the interpreter shown above to +work with this action class and prove (using ``assert_norm``) that a +program that contains two parallel atomic increments increments the +state twice. + +`Exercise File <../code/exercises/Part2.AtomicIncrement.fst>`__ + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.Par.fst + :language: fstar + :start-after: //SNIPPET_START: atomic increment$ + :end-before: //SNIPPET_END: atomic increment$ + +-------------------------------------------------------------------------------- + +Looking ahead ++++++++++++++ + +Writing correct programs with side-effects is hard, particularly when +those effects include features like mutable state and concurrency! + +What we've seen here is that although we've been able to model the +semantics of these programs, proving that they work correctly is +non-trivial. Further, while we have defined interpreters for our +programs, these interpreters are far from efficient. In practice, one +usually resorts to things like shared-memory concurrency to gain +performance and our interpreters, though mathematically precise, are +horribly slow. + +Addressing these two topics is the main purpose of F*'s user-defined +effect system, a big part of the language which we'll cover in a +subsequent section. The effect system aims to address two main needs: + + * Proofs of effectful programs: The effect system enables developing + effectful programs coupled with *program logics* that enable + specifying and proving program properties. We'll learn about many + different kinds of logics that F* libraries provide, ranging from + classical Floyd-Hoare logics for sequential programs, relational + logics for program equivalence, weakest precondition calculi, and + separation logics for concurrent and distributed programs. + + * Effect abstraction: Although programs can be specified and proven + against a clean mathematical semantics, for efficient execution, + F* provides mechanisms to hide the representation of an effect so + that effectful programs can be compiled efficiently to run with + native support for effects like state, exceptions, concurrency, + and IO. diff --git a/book/part2/part2_phoas.rst b/book/part2/part2_phoas.rst new file mode 100644 index 00000000000..e362ab917e8 --- /dev/null +++ b/book/part2/part2_phoas.rst @@ -0,0 +1,257 @@ +.. _Part2_phoas: + +Higher-order Abstract Syntax +============================ + +In the previous chapter, we looked at a *deep embedding* of the simply +typed lambda calculus (STLC). The encoding is "deep" in the sense that +we used an inductive type to represent the *syntax* of the lambda +calculus in F*, and then defined and proved some properties of its +semantics represented mathematically in F*. + +Another way to embed a language like the STLC in F* is a *shallow +embedding*. F* is itself a functional programming language, and it has +a type system that is certainly powerful enough to represent simply +typed terms, so why not use lambda terms in F* itself to represent +STLC, rather than merely encoding STLC's abstract syntax in F*. This +kind of encoding is called a shallow embedding, where we use semantic +constructs in the host (or meta) language (F*) to represent analogous +features of the embedded (or object) language (STLC, in our example). + +In this chapter, we look at a particularly elegant technique for doing +this called *higher-order abstract syntax* (or HOAS). For more +background about this, a `2008 paper by Adam Chlipala +`_ is a +good resource, though it develops a more sophisticated parametric +version. + +Our small case study in HOAS is meant to illustrate the use of +inductive types with non-trivial indexes while also containing +strictly positive functions as arguments, and also a bit of type-level +computation. + +Roadmap ++++++++ + +The type ``typ`` below represents the types we'll use in our STLC +object language, i.e., the base types ``Bool`` and ``Int``, and +function types ``Arrow t1 t2``. + +.. literalinclude:: ../code/Part2.HOAS.fst + :language: fstar + :start-after: //SNIPPET_START: typ$ + :end-before: //SNIPPET_END: typ$ + +This is analogous to our representation of STLC types in the deep +embedding of the previous chapter. + +Where things get interesting is in the representation of STLC terms +and their semantics. To set the goal posts, we want to + + 1. Give an interpretation of STLC types into F* types, by defining a + function ``denote_typ : typ -> Type`` + + 2. Define a type ``term t``, to represent well-typed STLC + terms whose type is ``t:typ`` + + 3. Give an intepretation of STLC terms in to F* terms of the + suitable type, i.e., define a function ``denote_term (#t:typ) + (e:term t) : denote_typ t``, proving that every well-typed STLC + term at type ``t`` can be represented in F* as a function of type + ``denote_typ t``. + +Such a result would encompass the type soundness results we proved in +the previous chapter (proving that well-typed programs can always make +progress), but would go substantially further to show that the +reduction of all such terms always terminates producing F* values that +model their semantics. + +.. _Part2_phoas_denotation: + +Denotation of types ++++++++++++++++++++ + +Step 1 in our roadmap is to give an interpreration of STLC types +``typ`` into F* types. This is easily done, as shown below. + +.. literalinclude:: ../code/Part2.HOAS.fst + :language: fstar + :start-after: //SNIPPET_START: denote_typ$ + :end-before: //SNIPPET_END: denote_typ$ + +We have here a recursive function that computes a *Type* from its +argument. This is may seem odd at first, but it's perfectly legal in a +dependently typed language like F*. + +The function ``denote_typ`` The interpretation of ``Bool`` and ``Int`` +are the F* type ``bool`` and ``int``, while the interpretation of +``Arrow`` is an F* arrow. Note, the function terminates because the +two recursive calls are on strict sub-terms of the argument. + + +Term representation +++++++++++++++++++++ + +The main difficulty in representing a language like STLC (or any +language with lambda-like variable-binding structure), is the question +of how to represent variables and their binders. + +In the deep embedding of the previous chapter, our answer to this +questions was very syntactic---variables are de Bruijn indexes, where, +at each occurrence, the index used for a variable counts the number of +lambdas to traverse to reach the binder for that variable. + +The HOAS approach to answering these questions is very different. The +idea is to use the binding constructs and variables already available +in the host language (i.e., lambda terms in F*) to represent binders +and variables in the object language (STLC). + +The main type in our representation of terms is the ``term`` defined +below. There are several clever subtleties here, which we'll try to +explain briefly. + +.. literalinclude:: ../code/Part2.HOAS.fst + :language: fstar + :start-after: //SNIPPET_START: term$ + :end-before: //SNIPPET_END: term$ + +First, the ``term`` type represents both the abstract syntax of the +STLC as well as its typing rules. We'll see in detail how this works, +but notice already that ``term`` is is indexed by a ``t:typ``, which +describes the type of encoded STLC term. The indexing structure +encodes the typing rules of STLC, ensuring that only well-typed STLC +terms can be constructed. + +The second interesting part is the use of ``denote_typ`` within the +syntax---variables and binders at a given STLC type ``t`` will be +represented by F* variables and binders of the corresponding F* type +``denote_typ t``. + + * ``Var`` : Variables are represented as ``Var #t n : term t``, + where ``n`` is a term of type ``denote_typ t``. + + * ``TT`` and ``FF``: The two boolean constansts are represented by + these constructors, both of type ``term Bool``, where the index + indicates that they have type ``Bool``. + + * ``I``: STLC integers are represented by tagged F* integers. + + * ``App``: To apply ``e1`` to ``e2`` in a well-typed way, we must + prove that ``e1`` has an arrow type ``TArrow t1 t2``, while ``e2`` + has type ``t2``, and the resulting term ``App e1 e2`` has type + ``t2``. Notice how the indexing structure of the ``App`` + constructor precisely captures this typing rule. + + * ``Lam``: Finally, and crucially, we represent STLC lambda terms + using F* functions, i.e., ``Lam f`` has type ``Arrow t1 t2``, when + ``f`` is represented by an F* function from arguments of type + ``denote_typ t1``, to terms of type ``term t``. The ``Lam`` case + includes a function-typed field, but the type of that field, + ``denote_typ t1 -> term t2`` is strictly positive---unlike the the + ``dyn`` type, :ref:`shown earlier `. + + +Denotation of terms ++++++++++++++++++++ + +Finally, we come to Step 3 (below), where we give an interpretation to +``term t`` as an F* term of type ``denote_typ t``. The trickiest part +of such a interpretation is to handle functions and variables, but +this part is already done in the representation, since these are +already represented by the appropriate F* terms. + +.. literalinclude:: ../code/Part2.HOAS.fst + :language: fstar + :start-after: //SNIPPET_START: denote_term$ + :end-before: //SNIPPET_END: denote_term$ + +Let's look at each of the cases: + + * The ``Var`` case is easy, since the variable ``x`` is already + interpreted into an element of the appropriate F* type. + + * The constants ``TT``, ``FF``, and ``I`` are easy too, since we can + just interpret them as the suitable boolean or integer constants. + + * For the ``App #t1 #t2 f a`` case, we recursively interpret ``f`` + and ``a``. The type indices tell us that ``f`` must be interpreted + into an F* function of type ``denote_typ t1 -> denote_typ t2`` and + that ``denote_term a`` has type ``denote_typ t1``. So, we can + simply apply the denotation of ``f`` to the denotation of ``a`` to + get a term of type ``denote_typ t2``. In other words, function + application in STLC is represented semantically by function + application in F*. + + * Finally, in the ``Lam #t1 #t2 f`` case, we need to produce a term + of type ``denote_typ t1 -> denote_typ t2``. So, we build an F* + lambda term (where the argument ``x`` has type ``denote_typ t1``), + and in the body we apply ``f x`` and recursively call + ``denote_term`` on ``f x``. + +If that felt a little bit magical, it's because it almost is! We've +defined the syntax, typing rules, and an interpreter that doubles as a +denotational semantics for the STLC, and proved everything sound in +around 30 lines of code and proof. By picking the right +representation, everything just follows very smoothly. + +Termination +----------- + +You may be wondering why F* accepts that ``denote_term e`` +terminates. There are three recursive calls to consider + + * The two calls in the ``App`` case are easy: The recursive calls + are on strict sub-terms of ``App f a``. + + * In the ``Lam f`` case, we have one recursive call ``denote_term + (f x)``, and F* accepts that ``f x`` is strictly smaller than ``Lam + f``. This is an instance of the sub-term ordering on inductive + types explained earlier, as part of F*'s ``precedes`` relation, + :ref:`explained earlier `. + +For a bit of intuition, one way to understand the type ``term`` is by +thinking of it as a tree of finite depth, but possibly infinite width: + + * The leaves of the tree are the ``Var``, ``TT``, and ``FF`` cases. + + * The internal node ``App e0 e1`` composes two sub-trees, ``e0`` and + ``e1``. + + * The internal node ``Lam #t1 #t2 f`` composes a variable number of + sub-trees, where the number of sub-trees depends on the parameter + ``t1``. For example: + + - If ``t1 = Unit``, then ``f : unit -> term v t``, i.e., there is + only one child node, ``f()``. + + - If ``t1 = Bool``, then ``f : bool -> term v t``, i.e., there are + two children, ``f true`` and ``f false``. + + - if ``t1 = Int``, then ``f : int -> term v t``, i.e., there are + infinitely many children, ``..., f -1, f 0, f 1, ...``. + +With this intuition, informally, it is safe to recursively call +``denote_term e`` on any of the children of ``e``, since the depth of +the tree will decrease on each recursive call. Hence the call +``denote_term (f x)`` terminates. + +We'll revisit termination arguments for recursive functions more +formally in a subsequent chapter on :ref:`well-founded recursion +`. + +Exercises ++++++++++ + +Giving a semantics to STLC is just the tip of the iceberg. There's a +lot more one can do with HOAS and Chlipala's paper gives lots of +examples and sample code in Coq. + +For several more advanced exercises, based on the definitions shown +below, try reconstructing other examples from Chlipala's paper, +including a proof of correctness of a compiler implementing a +continuation-passing style (CPS) transformation of STLC. + +`Exercise file <../code/Part2.PHOAS.fst>`_ + +.. literalinclude:: ../code/Part2.PHOAS.fst + :language: fstar diff --git a/book/part2/part2_stlc.rst b/book/part2/part2_stlc.rst new file mode 100644 index 00000000000..3fe599ad3af --- /dev/null +++ b/book/part2/part2_stlc.rst @@ -0,0 +1,675 @@ +.. _Part2_stlc: + +Simply Typed Lambda Calculus +============================ + +In this chapter, we look at how inductively defined types can be used +to represent both raw data, inductively defined relations, and proofs +relating the two. + +By way of illustration, we develop a case study in the simply typed +lambda calculus (STLC), a very simple programming language which is +often studied in introductory courses on the semantics of programming +languages. Its syntax, type system, and runtime behavior can be +described in just a few lines. The main result we're interested in +proving is the soundness of the type system, i.e., that if a program +type checks then it can be executed safely without a certain class of +runtime errors. + +If you haven't seen the STLC before, there are several good resources +for it available on the web, including the `Software Foundations book +`_, +though we'll try to keep the presentation here as self-contained as +possible. Thanks to Simon Forest, Catalin Hritcu, and Simon Schaffer +for contributing parts of this case study. + +Syntax +++++++ + +The syntax of programs :math:`e` is defined by the context-free +grammar shown below. + +.. math:: + + e~::=~()~|~x~|~\lambda x:t. e_0~|~e_0~e_1 + +This can be read as follows: a program :math:`e` is either + + * the unit value :math:`()`; + + * a variable :math:`x`; + + * a lambda term :math:`\lambda x:t. e_0` associating a variable + :math:`x` to a type :math:`t` and a some sub-program :math:`e_0`; + + * or, the application of the sub-program :math:`e_0` to another + sub-program :math:`e_1`. + +The syntax of the type annotation :math:`t` is also very simple: + +.. math:: + + t~::=~\mathsf{unit}~|~t_0 \rightarrow t_1 + +A type :math:`t` is either + + * the :math:`\mathsf{unit}` type constant; + + * or, arrow type :math:`t_0 \rightarrow t_1` formed from two smaller types + :math:`t_0` and :math:`t_1` + +This language is very minimalistic, but it can be easily extended with +some other forms, e.g., one could add a type of integers, integer +constants, and operators like addition and subtraction. We'll look at +that as part of some exercises. + +We'll define the syntax of types and programs formally in F* as a pair +of simple inductive datatypes ``typ`` (for types) and ``exp`` (for +programs or expressions) with a constructor for each of the cases +above. + +The main subtlety is in the representation of variables. For example, +ignoring the type annotations, in the term +:math:`\lambda x. (\lambda x. x)` the inner lambda binds *a different* +:math:`x` than the outer one, i.e., the term is equivalent to +:math:`\lambda x. (\lambda y. y)` and our representation of programs +must respect this this convention. We'll use a technique called de +Bruijn indices, where the names of the variables are no longer +significant and instead each variable is represented by a natural +number describing the number of :math:`\lambda` binders that one must +cross when traversing a term from the occurrence of the variable to +that variable's :math:`\lambda` binder. + +For example, the terms :math:`\lambda x. (\lambda x. x)` and +:math:`\lambda x. (\lambda y. y)` are both represented as +:math:`\lambda _. (\lambda _. 0)`, since the inner occurrence of +:math:`x` is associated with the inner :math:`\lambda`; while +:math:`\lambda x. (\lambda y. (\lambda z. x))` is represented as +:math:`\lambda _. (\lambda _. (\lambda _. 2)`, since from the inner +occurrence of :math:`x` one must skip past :math:`2` :math:`\lambda`s +to reach the :math:`\lambda` associated with :math:`x`. Note, the +variable names are no longer significant in de Bruijn's notation. + +Representing types +------------------ + +The inductive type ``typ`` defined below is our representation of +types. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: typ$ + :end-before: //SNIPPET_END: typ$ + +This is entirely straightforward: a constructor for each case in our +type grammar, as described above. + +Representing programs +--------------------- + +The representation of program expressions is shown below: + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: exp$ + :end-before: //SNIPPET_END: exp$ + +This too is straightforward: a constructor for each case in our +program grammar, as described above. We use a ``nat`` to represent +variables ``var`` and ``ELam`` represents an annotated lambda term of +the form :math:`\lambda _:t. e`, where the name of the binder is +omitted, since we're using de Bruijn's representation. + +Runtime semantics ++++++++++++++++++ + +STLC has just one main computation rule to execute a program---the +function application rule or a :math:`\beta` reduction, as shown below: + +.. math:: + + (\lambda x:t. e_0)~e_1 \longrightarrow e_0 [x \mapsto e_1] + +This says that when a :math:`\lambda` literal is applied to an +argument :math:`e_1` the program takes a single step of computation to +the body of the lambda literal :math:`e_0` with every occurrence of +the bound variable :math:`x` replaced by the argument :math:`e_1`. The +substituion has to be careful to avoid "name capture", i.e., +substituting a term in a context that re-binds its free variables. For +example, when substituting :math:`y \mapsto x` in +:math:`\lambda x. y`, one must make sure that the resulting term is +**not** :math:`\lambda x. x`. Using de Bruijn notation will help us +make precise and avoid name capture. + +The other computation rules in the language are inductively defined, +e.g., :math:`e_0~e_1` can take a step to :math:`e_0'~e_1` if +:math:`e_0 \longrightarrow e_0'`, and similarly for :math:`e_1`. + +By choosing these other rules in different ways one obtains different +reduction strategies, e.g., call-by-value or call-by-name etc. We'll +leave the choice of reduction strategy non-deterministic and represent +the computation rules of the STLC as an indexed inductive type, ``step +e e'`` encoding one or more steps of computation. + +Formalizing an Operational Semantics +------------------------------------ + +The inductive type ``step`` below describes a single step of +computation in what is known as a "small-step operational +semantics". The type ``step e e'`` is a relation between an initial +program ``e`` and a program ``e'`` that results after taking one step +of computation on some sub-term of ``e``. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: step$ + :end-before: //SNIPPET_END: step$ + +-------------------------------------------------------------------------------- + + * The constructor ``Beta`` represents the rule for :math:`\beta` + reduction. The most subtle part of the development is defining + ``subst`` and ``sub_beta``---we'll return to that in detail + shortly. + + * ``AppLeft`` and ``AppRight`` allow reducing either the left- or + right-subterm of ``EApp e1 e2``. + + +Exercise +^^^^^^^^ + +Define an inductive relation ``steps : exp -> exp -> Type`` for the +transitive closure of ``step``, representing multiple steps of +computation. + +Use this `exercise file <../code/exercises/Part2.STLC.fst>`_ for all +the exercises that follow. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: steps$ + :end-before: //SNIPPET_END: steps$ + +-------------------------------------------------------------------------------- + +Substitution: Failed Attempt +---------------------------- + +Defining substitution is the trickiest part of the system. Our first +attempt will convey the main intuitions, but F* will refuse to accept +it as well-founded. We'll then enrich our definitions to prove that +substitution terminates. + +We'll define a substitution as a total function from variables ``var`` +to expressions ``exp``. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: sub0$ + :end-before: //SNIPPET_END: sub0$ + +These kind of substitutions are sometimes called "parallel +substitutions"---the each variable is substituted independently of the +others. + +When doing a :math:`\beta` reduction, we want to substitute the +variable associated with de Bruijn index ``0`` in the body of the +function with the argument ``e`` and then remove the :math:`\lambda` +binder---``sub_beta0`` does just that, replacing variable ``0`` with +``e`` and shifting other variables down by ``1``, since the +:math:`\lambda` binder of the function is removed. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: sub_beta0$ + :end-before: //SNIPPET_END: sub_beta0$ + +The function ``subst s e`` applies the substitution ``s`` to ``e``: + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: subst0$ + :end-before: //SNIPPET_END: subst0$ + +* The ``EUnit`` case is trivial---there are no variables to substitute. + +* In the variable case ``subst0 s (EVar x)`` just applies ``s`` to ``x``. + +* In the ``EApp`` case, we apply the substitution to each sub-term. + +* The ``ELam`` case is the most interesting. To apply the substitution + ``s`` to the body ``e1``, we have to traverse a binder. The mutally + recursive function ``sub_elam0 s`` adjusts ``s`` to account for this + new binder, which has de Bruijn index ``0`` in the body ``e1`` (at + least until another binder is encountered). + + - In ``sub_elam0``, if we are applying ``s`` to the newly bound + variable at index ``0``, then we leave that variable unchanged, + since ``s`` cannot affect it. + + - Otherwise, we have a variable with index at least ``1``, + referencing a binder that is bound in an outer scope; so, we shift + it down and apply ``s`` to it, and then increment all the + variables in the resulting term (using ``sub_inc0``) to avoid capture. + +This definition of substitution is correct, but F* refuses to accept +it since we have not convinced the typechecker that ``subst0`` and +``sub_elam0`` actually terminate. In fact, F* complains in two +locations about a failed termination check. + +.. note:: + + This definition is expected to fail, so the ``[@@expect_failure + [19;19]]`` attribute on the definition asks F* to check that the + definition raises Error 19 twice. We'll look in detail as to why it + fails, next. + +Substitution, Proven Total +-------------------------- + +Informally, let's try to convince ourselves why ``subst0`` and +``sub_elam0`` actually terminate. + +* The recursive calls in the ``EApp`` case are applied to strictly + smaller sub-terms (``e0`` and ``e1``) of the original term ``e``. + +* In the ``ELam`` case, we apply ``subst0`` to a smaller sub-term + ``e1``, but we make a mutally recursive call to ``sub_elam0 s`` + first---so we need to check that that call terminates. This is the + first place where F* complains. + +* When calling ``sub_elam0``, it calls back to ``subst0`` on a + completely unrelated term ``s (y - 1)``, and F* complains that this + may not terminate. But, thankfully, this call makes use only of the + ``sub_inc0`` substitution, which is just a renaming substitution and + which does not make any further recursive calls. Somehow, we have to + convince F* that a recursive call with a renaming substitution is + fine. + +To distinguish renamings from general substitutions, we'll use an +indexed type ``sub r``, shown below. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: sub$ + :end-before: //SNIPPET_END: sub$ + +* ``sub true`` is the type of renamings, substitutions that map + variables to variables. + +* ``sub false`` are substitutions that map at least one variable to a + non-variable. + +It's easy to prove that ``sub_inc`` is a renaming: + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: sub_inc$ + :end-before: //SNIPPET_END: sub_inc$ + +The function ``sub_beta`` shown below is the analog of ``sub_beta0``, +but with a type that tracks whether it is a renaming or not. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: sub_beta$ + :end-before: //SNIPPET_END: sub_beta$ + +* The type says that ``sub_beta e`` is a renaming if and only if ``e`` + is itself a variable. + +* Proving this type, particularly in the case where ``e`` is not a + variable requires proving an existentially quantified formula, i.e., + ``exists x. ~(EVar (subst_beta e) x)``. As mentioned + :ref:`previously `, the SMT solver cannot + always automatically instantiate existential quantifiers in the + goal. So, we introduce the existential quantifier explicitly, + providing the witness ``0``, and then the SMT solver can easily + prove ``~(EVar (subst_beta e) 0)``. + +Finally, we show the definitions of ``subst`` and ``sub_elam`` +below---identical to ``subst0`` and ``sub_elam0``, but enriched with +types that allow expressing a termination argument to F* using a +4-ary lexicographic ordering. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: subst$ + :end-before: //SNIPPET_END: subst$ + +Let's analyze the recursive calls of ``subst`` and ``subst_elam`` to +see why this order works. + +* Cases of ``subst``: + + - The ``EUnit`` and ``EVar`` cases are trivial, as before. + + - In ``EApp``, ``e`` is definitely not a variable, so ``bool_order + (EVar? e)`` is ``1``. if ``e1`` (respectively ``e2``) are + variables, then this recursive call terminates, the lexicographic + tuple ``(0, _, _, _) << (1, _, _, _)``, regardles of the other + values. Otherwise, the last component of the tuple decreases + (since ``e1`` and ``e2`` are proper sub-terms of ``e``), while + none of the other components of the tuple change. + + - The call to ``sub_elam s`` in ``ELam`` terminates because the + third component of the tuple decreases from ``1`` to ``0``, while + the first two do not change. + + - The final recursive call to ``subst`` terminates for similar + reasons to the recursive calls in the ``EApp`` case, since the + type of ``sub_elam`` guarantees that ``sub_elam s`` is renaming if + an only of ``s`` is (so the ``r`` bit does not change). + +* Cases of ``sub_elam``, in the recursive call to ``subst sub_inc (s + (y - 1))``, we have already proven that ``sub_inc`` is a + renaming. So, we have two cases to consider: + + - If ``s (y - 1)`` is a variable, then ``bool_order (EVar? e)``, the + first component of the decreases clause of ``subst`` is ``0``, + which clearly precedes ``1``, the first component of the decreases + clause of ``subst_elam``. + + - Otherwwise, ``s (y - 1)`` is not a variable, so ``s`` is + definitely not a renaming while ``sub_inc`` is. So, the second + second component of the decreases clause decreases while the first + component is unchanged. + +Finally, we need to prove that ``sub_elam s`` is a renaming if and +only if ``s`` is. For this, we need two things: + +* First, strengthen the type of ``subst s`` to show that it maps + variables to variables if and only if ``r`` is a renaming, + +* Second, we need to instantiate an exisential quantifier in + ``sub_elam``, to show that if ``s`` is not a renaming, then it must + map some ``x`` to a non-variable and, hence, ``sub_elam s (x + 1)`` + is a non-variable too. One way to do this is by asserting this fact, + which is a sufficient hint to the SMT solver to find the + instantiation needed. Another way is to explicitly introduce the + existential, as in the exercise below. + +In summary, using indexed types combined with well-founded recursion +on lexicographic orderings, we were able to prove our definitions +total. That said, coming up with such orderings is non-trivial and +requires some ingenuity, but once you do, it allows for relatively +compact definitions that handle both substiutions and renamings. + +Exercise +^^^^^^^^ + +Remove the first component of the decreases clause of both definitions +and revise the definitions to make F* accept it. + +Your solution should have signature + +.. code-block:: fstar + + let rec subst1 (#r:bool) + (s:sub r) + (e:exp) + : Tot (e':exp { r ==> (EVar? e <==> EVar? e') }) + (decreases %[bool_order r; + 1; + e]) + ... + + and sub_elam1 (#r:bool) (s:sub r) + : Tot (sub r) + (decreases %[bool_order r; + 0; + EVar 0]) + + +.. container:: toggle + + .. container:: header + + **Hint** + + Inline a case of ``subst`` in ``subst_elam``. + The answer is included with the next problem below. + +-------------------------------------------------------------------------------- + +Replace the assertion in ``subst_elam`` with a proof that explicitly +introduces the existential quantifier. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: subst1$ + :end-before: //SNIPPET_END: subst1$ + +-------------------------------------------------------------------------------- + +Type system ++++++++++++ + +If when running a program, if one ends up with an term like +:math:`()~e` (i.e., some non-function term like :math:`()` being used +as if it were a function) then a runtime error has occurred and the +program crashes. A type system for the simply-typed lambda calculus is +designed to prevent this kind of runtime error. + +The type system is an inductively defined relation ``typing g e t`` +between a + + * typing environment ``g:env``, a partial map from variable indexes + in a particular scope to their annotated types; + + * a program expression ``e:exp``; + + * and its type ``t:typ``. + +Environments +------------ + +The code below shows our representation of typing environments +``env``, a total function from variable indexes ``var`` to ``Some t`` +or ``None``. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: env$ + :end-before: //SNIPPET_END: env$ + +* The ``empty`` environment maps all variables to ``None``. + +* Extending an an environment ``g`` associating a type ``t`` with a + new variable at index ``0`` involves shifting up the indexes of all + other variables in ``g`` by ``1``. + +.. _Part2_stlc_typing: + +Typing Relation +--------------- + +The type system of STLC is defined by the inductively defined relation +``typing g e t`` shown below. A value of ``typing g e t`` is a +derivation, or a proof, that ``e`` has type ``t`` in the environment +``g``. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: typing$ + :end-before: //SNIPPET_END: typing$ + +* The type does not support decidable equality, since all its + constructors contain a field ``g:env``, a function-typed value + without decidable equality. So, we mark the inductive with the + ``noeq`` qualifier, :ref:`as described previously + `. + +* ``TyUnit`` says that the unit value ``EUnit`` has type ``TUnit`` in + all environments. + +* ``TyVar`` says that a variable ``x`` is well-typed only in an + environment ``g`` that binds its type to ``Some t``, in which case, + the program ``EVar x`` has type ``t``. This rule ensures that no + out-of-scope variables can be used. + +* ``TyLam`` says that a function literal ``ELam t e1`` has type ``TArr + t t'`` in environment ``g``, when the body of the function ``e1`` + has type ``t'`` in an environment that extends ``g`` with a binding + for the new variable at type ``t`` (while shifting and retaining all + other ariables). + +* Finally, ``TyApp`` allows applying ``e1`` to ``e2`` only when ``e1`` + has an arrow type and the argument ``e2`` has the type of the formal + parameter of ``e1``---the entire term has the return type of ``e1``. + +Progress +++++++++ + +It's relatively easy to prove that a well-typed non-unit or lambda +term with no free variables can take a single step of +computation. This property is known as *progress*. + + +Exercise +-------- + +State and prove progress. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: progress$ + :end-before: //SNIPPET_END: progress$ + +-------------------------------------------------------------------------------- + +Preservation +++++++++++++ + +Given a well-typed term satisfying ``typing g e t`` and ``steps e e'``, +we would like to prove that ``e'`` has the same type as ``e``, i.e., +``typing g e' t``. This property is known as *preservation* (or +sometimes *subject reduction*). When taken in combination with +*progress*, this allows us to show that a well-typed term can keep +taking a step until it reaches a value. + +The proof below establishes preservation for a single step. + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: preservation_step$ + :end-before: //SNIPPET_END: preservation_step$ + +* Since we know the computation takes a step, the typing derivation + ``ht`` must be an instance of ``TyApp``. + +* In the ``AppLeft`` and ``AppRight`` case, we can easily use the + induction hypothesis depending on which side actually stepped. + +* The ``Beta`` case is the most interesting and requires a lemma about + substitutions preserving typing. + +The substitution lemma follows: + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: substitution$ + :end-before: //SNIPPET_END: substitution$ + +It starts with a notion of typability of substitutions, ``subst_typing +s g1 g2``, which that if a variable ``x`` has type ``g1 x``, then ``s +x`` must have that same type in ``g2``. + +The substitution lemma lifts this notion to expressions, stating that +applying a well-typed substitution ``subst_typing s g1 g2`` to a term +well-typed in ``g1`` produces a term well-typed in ``g2`` with the +same type. + +Exercise +-------- + +Use the substitution lemma to state and prove the +``substitution_beta`` lemma used in the proof of preservation. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: substitution_beta$ + :end-before: //SNIPPET_END: substitution_beta$ + +-------------------------------------------------------------------------------- + +Exercise +-------- + +Prove a preservation lemma for multiple steps. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: preservation$ + :end-before: //SNIPPET_END: preservation$ + +-------------------------------------------------------------------------------- + +Exercise +++++++++ + +Prove a type soundness lemma with the following statement: + +.. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: soundness_stmt$ + :end-before: //SNIPPET_END: soundness_stmt$ + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.STLC.fst + :language: fstar + :start-after: //SNIPPET_START: soundness_sol$ + :end-before: //SNIPPET_END: soundness_sol$ + +-------------------------------------------------------------------------------- + +Exercise +++++++++ + +Add a step for reduction underneath a binder and prove the system +sound. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part2.STLC.Strong.fst + :language: fstar diff --git a/book/part2/part2_universes.rst b/book/part2/part2_universes.rst new file mode 100644 index 00000000000..cde7c247a3a --- /dev/null +++ b/book/part2/part2_universes.rst @@ -0,0 +1,759 @@ +.. _Part2_universes: + + +Universes +========= + +As mentioned :ref:`earlier `, ``Type`` is the +type of types. So, one might ask the question, what is the type of +``Type`` itself? Indeed, one can write the following and it may appear +at first that the type of ``Type`` is ``Type``. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: ty$ + :end-before: //SNIPPET_END: ty$ + +However, behind the scenes, F* actually has a countably infinite +hierarchy of types, ``Type u#0``, ``Type u#1``, ``Type u#2``, ..., and +the type of ``Type u#i`` is actually ``Type u#(i + 1)``. The ``u#i`` +suffixes are called *universe levels* and if you give F* the following +option, it will actually show you the universe levels it inferred when +it prints a term. + +.. code-block:: fstar + + #push-options "--print_universes" + +With this option enabled, in fstar-mode.el, the F* emacs plugin, +hovering on the symbol ``ty`` prints ``Type u#(1 + _)``, i.e., the +type of ``ty`` is in a universe that is one greater than some universe +metavariable ``_``, i.e., ``ty`` is universe *polymorphic*. But, we're +getting a bit ahead of ourselves. + +In this chapter, we'll look at universe levels in detail, including +why they're necessary to avoid paradoxes, and showing how to +manipulate definitions that involve universes. For the most part, F* +infers the universe levels of a term and you don't have to think too +much about it---in fact, in all that we've seen so far, F* inferred +universe levels behind the scenes and we haven't had to mention +them. Though, eventually, they do crop up and understanding what they +mean and how to work with them becomes necessary. + +Other resources to learn about universes: + + * The Agda manual has a nice `chapter on universes + `_, + including universe polymorphism. + + * This chapter from Adam Chlipala's `Certified Programming with + Dependent Types + `_ + describes universes in Coq. While it also provides useful + background, F*'s universe system is more similar to Agda's and + Lean's than Coq's. + + +Basics +------ + +A universe annotation on a term takes the form ``u#l``, where ``l`` is +a universe level. The universe levels are terms from the following +grammar: + +.. code-block:: + + k ::= 0 | 1 | 2 | ... any natural number constant + l ::= k universe constant + | l + k | k + l constant offset from level l + | max l1 l2 maximum of two levels + | a | b | c | ... level variables + + +Let's revisit our first example, this time using explicit universe +annotations to make things clearer. + +We've defined, below, instances of ``Type`` for universe levels ``0, +1, 2`` and we see that each of them has a type at the next level. The +constant ``Type u#0`` is common enough that F* allows you to write +``Type0`` instead. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: ty_constants$ + :end-before: //SNIPPET_END: ty_constants$ + +If you try to define ``ty_bad`` below, F* complains with the following +error: + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: ty_bad$ + :end-before: //SNIPPET_END: ty_bad$ + +.. code-block:: + + Expected expression of type "Type0"; got expression "Type0" of type "Type u#1" + +The restriction that prevents a ``Type`` from being an inhabitant of +itself if sometimes called *predicativity*. The opposite, +*impredicativity*, if not suitably restricted, usually leads to +logical inconsistency. F* provides a limited form of impredicativity +through the use of ``squash`` types, which we'll see towards the end +of this chapter. + +.. note:: + + That said, if we didn't turn on the option ``--print_universes``, the + error message you get may be, sadly, bit baffling: + + .. code-block:: + + Expected expression of type "Type"; got expression "Type" of type "Type" + + Turning on ``--print_universes`` and ``--print_implicits`` is a + good way to make sense of type errors where the expected type and + the type that was computed seem identical. + + +Now, instead of defining several constants like ``ty0, ty1, ty2`` +etc., F* definitions can be *universe polymorphic*. Below, we define +``ty_poly`` as ``Type u#a``, for any universe variable ``a``, and so +``ty`` has type ``Type u#(a + 1)``. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: ty_poly$ + :end-before: //SNIPPET_END: ty_poly$ + +One way to think of ``ty_poly`` is as a "definition template" +parameterized by the by the universe-variable ``a``. One can +instantiate ``ty_poly`` with a specific universe level ``l`` (by +writing ``ty_poly u#l``) and obtain a copy of its definition +specialized to level ``l``. F* can prove that instantiation of +``ty_poly`` are equal to the non-polymorphic definitions we had +earlier. As the last example shows, F* can usually infer the universe +instantiation, so you often don't need to write it. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: ty_poly_assert$ + :end-before: //SNIPPET_END: ty_poly_assert$ + +Universe computations for other types +------------------------------------- + +Every type in F* lives in a particular universe. For example, here are +some common types in ``Type u#0``. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: some common types$ + :end-before: //SNIPPET_END: some common types$ + + +**Universe of an arrow type**: In general, the universe of an arrow +type ``x:t -> t'`` is the the maximum of the universe of ``t`` and +``t'``. + +This means that functions that are type-polymorphic live in higher +universes. For example, the polymorphic identity function that we saw +in an :ref:`earlier section `, is +actually also polymorphic in the universe level of its type argument. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: poly_id$ + :end-before: //SNIPPET_END: poly_id$ + +That is, the type of the identity function ``id`` is ``id_t`` or +``a:Type u#i -> a -> a``---meaning, for all types ``a`` in +universe ``Type u#i``, ``id a`` is function of type ``a -> a``. + +Now, ``id_t`` is itself a type in universe ``Type u#(i + 1)``, and +since the ``id`` function can be applied to types in any universe, it +can be applied to ``id_t`` too. So, it may look like this allows one +to write functions that can be applied to themselves---which would be +bad, since that would allow one to create infinite loops and break +F*'s logic. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: seemingly_self_application$ + :end-before: //SNIPPET_END: seemingly_self_application$ + +However, if we write out the universe levels explicitly, we see that +actually we aren't really applying the ``id`` function to +itself. Things are actually stratified, so that we are instead applying an +instance of ``id`` at universe ``u#(i + 1)`` to the instance of ``id`` +at universes ``u#i``. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: stratified_application$ + :end-before: //SNIPPET_END: stratified_application$ + + +One intuition for what's happening here is that there are really +infinitely many instances of the F* type system nested within each +other, with each instance forming a universe. Type-polymorphic +functions (like ``id``) live in some universe ``u#(a + 1)`` and are +parameterized over all the types in the immediately preceding universe +``u#a``. The universe levels ensure that an F* function within +universe level ``u#a`` cannot consume or produce terms that live in +some greater universe. + +Universe level of an inductive type definition +.............................................. + +F* computes a universe level for inductive type definitions. To +describe the rules for this in full generality, consider again the +general form of an inductive type definition, shown first :ref:`here +`, but this time with the universe level of each +type constructor made explicit, i.e., :math:`T_i` constructs a type in +universe :math:`\mathsf{Type~u\#l_i}` + +.. math:: + + \mathsf{type}~T_1~\overline{(x_1:p_i)} : \overline{y_1:q_1} \rightarrow \mathsf{Type}~u\#l_1 = \overline{\bar D_1 : t_1} \\ + \mathsf{and}~T_n~\overline{(x_n:p_n)} : \overline{y_n:q_n} \rightarrow \mathsf{Type}~u\#l_n = \overline{\bar D_n : t_n} \\ + +Recall that each type constructor :math:`T_i` has zero or more *data +constructors* :math:`\overline{D_i:t_i}` and for each data constructor +:math:`D_{ij}`, its type :math:`t_{ij}` must be of the form +:math:`\overline{z_{ij}:s_{ij}} \rightarrow T_i~\bar{x_i}~\bar{e}` + +In addition to checking, as usual, that the each :math:`t_{ij}` is +well-typed, F* also checks the universe levels according to the +following rule: + + * Assuming that each :math:`T_i` has universe level :math:`l_i`, for + every data constructor :math:`D_{ij}`, and for each of its + arguments :math:`z_{ijk} : s_{ijk}`, check :math:`s_{ijk} : + \mathsf{Type}~u\#l_{ijk}` and :math:`l_{ijk} \leq l_i`. + +In other words, the universe level of each type constructor must not +be less than the universe of any of the fields of data constructors. + +In practice, F* infers the universe levels :math:`l_1, \ldots, l_n`, by +collecting level-inequality constraints and solving them using the +``max`` operator on universe levels, i.e., :math:`l_i` is set to +:math:`max_{jk}~l_{ijk}`, the maximum of the universe levels of all +the fields of the constructors :math:`\overline{D_i : t_i}`. Let's +look at some examples. + +The ``list`` type ++++++++++++++++++ + +The ``list`` type below is parameterized by ``a:Type u#a`` and +constructs a type in the same universe. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: list$ + :end-before: //SNIPPET_END: list$ + +* The ``Nil`` constructor has no fields, so it imposes no + constraints on the universe level of ``list a``. + +* The ``Cons`` constructor has two fields. Its first field ``hd`` + has type ``a: Type u#a``: we have a constraint that ``u#a`` :math:`\leq` ``u#a``; and + the second field, by assumption, has type ``list a : Type u#a``, + and again we have the constraint ``u#a`` :math:`\leq` ``u#a``. + +F* infers the minimum satisfiable universe level assignment, by +default. But, there are many solutions to the inequalities, and if +needed, one can use annotations to pick another solution. For example, +one could write this, though it rarely makes sense to pick a universe +for a type higher than necessary (see :ref:`this section ` for an exception). + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: list'$ + :end-before: //SNIPPET_END: list'$ + +.. note:: + + Universe level variables are drawn from a different namespace than + other variables. So, one often writes ``a:Type u#a``, where ``a`` + is a regular variable and ``u#a`` is the universe level of the type + of ``a``. + +The ``pair`` type ++++++++++++++++++ + +The ``pair`` type below is parameterized by ``a:Type u#a`` and +``b:Type u#b`` and constructs a type in ``u#(max a b)``. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: pair$ + :end-before: //SNIPPET_END: pair$ + +* The ``fst`` field is in ``u#a`` and so we have ``u#a`` :math:`\leq` ``u#(max a b)``. + +* The ``snd`` field is in ``u#b`` and so we have ``u#b`` :math:`\leq` ``u#(max a b)``. + +The ``top`` type ++++++++++++++++++ + +The ``top`` type below packages a value at any type ``a:Type u#a`` +with its type. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: top$ + :end-before: //SNIPPET_END: top$ + +* The ``a`` field of ``Top`` is in ``u#(a + 1)`` while the ``v`` field + is in ``u#a``. So, ``top`` itself is in ``u#(max (a + 1) a)``, + which simplifies to ``u#(a + 1)``. + +One intuition for why this is so is that from a value of type ``t : +top`` one can write a function that computes a value of type ``Type +u#a``, i.e., ``Top?.a t``. So, if instead we have ``top: Type u#a`` +and ``t:top``, then ``Top?.a : top -> Type u#a``, which would break +the stratification of universes, since from a value ``top`` in +universe ``u#a``, we would be able to project out a value in +``Type u#(a + 1)``, which leads to a paradox, as we'll see next. + +What follows is quite technical and you only need to know that the +universe system exists to avoid paradoxes, not how such paradoxes are +constructed. + +Russell's Paradox +----------------- + +Type theory has its roots in Bertrand Russell's `The Principles +of Mathematics +`_, which +explores the logical foundations of mathematics and set theory. In +this work, Russell proposed the paradoxical set :math:`\Delta` whose +elements are exactly all the sets that don't contain themselves and +considered whether or not :math:`\Delta` contained itself. This +self-referential construction is paradoxical since: + + * If :math:`\Delta \in \Delta`, then since the only sets in + :math:`\Delta` are the sets that don't contain themselves, we can + conclude that :math:`\Delta \not\in \Delta`. + + * On the other hand, if :math:`\Delta \not\in \Delta`, then since + :math:`\Delta` contains all sets that don't contain themselves, we + can conclude that :math:`\Delta \in \Delta`. + +To avoid such paradoxes, Russell formulated a stratified system of +types to prevent nonsensical constructions that rely on +self-reference. The universe levels of modern type theories serve much +the same purpose. + +In fact, as the construction below shows, if it were possible to break +the stratification of universes in F*, then one can code up Russell's +:math:`\Delta` set and prove ``False``. This construction is derived +from `Thorsten Altenkirch's Agda code +`_. Liam +O'Connor also provides `some useful context and comparison +`_. Whereas +the Agda code uses a special compiler pragma to enable unsound +impredicativity, in F* we show how a user-introduced axiom can +simulate impredicativity and enable the same paradox. + + +Breaking the Universe System +............................. + +Consider the following axioms that intentionally break the +stratification of F*'s universe system. We'll need the following +ingredients: + +1. A strictly positive type constructor ``lower`` that takes a type in + any universe ``a:Type u#a``, and returns a type in ``u#0``. Note, + we covered :ref:`strictly positive type functions, previously + `. + +.. literalinclude:: ../code/UnsoundUniverseLowering.fst + :language: fstar + :start-after: //SNIPPET_START: lower$ + :end-before: //SNIPPET_END: lower$ + +2. Assume there is a function called ``inject``, which takes value of + type ``x:a`` and returns value of type ``lower a``. + +.. literalinclude:: ../code/UnsoundUniverseLowering.fst + :language: fstar + :start-after: //SNIPPET_START: inject$ + :end-before: //SNIPPET_END: inject$ + +3. ``lower`` and ``inject`` on their own are benign (e.g., ``let lower + _ = unit`` and ``let inject _ = ()``). But, now if we assume we + have a function ``project`` that is the inverse of ``inject``, then + we've opened the door to paradoxes. + +.. literalinclude:: ../code/UnsoundUniverseLowering.fst + :language: fstar + :start-after: //SNIPPET_START: project$ + :end-before: //SNIPPET_END: project$ + +Encoding Russell's Paradox +........................... + +To show the paradox, we'll define a notion of ``set`` in terms of a +form of set comprehensions ``f: x -> set``, where ``x:Type u#0`` is +the domain of the comprehension, supposedly bounding the cardinality +of the set. We'll subvert the universe system by treating ``set`` as +living in universe ``u#0``, even though its constructor ``Set`` has a +field ``x:Type u#0`` that has universe level ``u#1`` + +.. literalinclude:: ../code/Russell.fst + :language: fstar + :start-after: //SNIPPET_START: set$ + :end-before: //SNIPPET_END: set$ + +This construction allows us to define many useful sets. For example, +the empty set ``zero`` uses the empty type ``False`` as the domain of +its comprehension and so has no elements; or the singleton set ``one`` +whose only element is the empty set; or the set ``two`` that contains +the empty set ``zero`` and the singleton set ``one``. + +.. literalinclude:: ../code/Russell.fst + :language: fstar + :start-after: //SNIPPET_START: zero,one,two$ + :end-before: //SNIPPET_END: zero,one,two$ + +One can also define set membership: A set ``a`` is a member of a set +``b``, if one can exhibit an element ``v`` of the domain type of +``b`` (i.e., ``(project b).x``), such that ``b``'s comprehension +``(project b).f`` applied to ``v`` is ``a``. + +For example, one can prove ``mem zero two`` by picking ``true`` for +``v`` and ``mem one two`` by picking ``false`` for +``v``. Non-membership is just the negation of membership. + +.. literalinclude:: ../code/Russell.fst + :language: fstar + :start-after: //SNIPPET_START: mem$ + :end-before: //SNIPPET_END: mem$ + +Now, we are ready to define Russell's paradoxical set +:math:`\Delta`. First, we define ``delta_big`` in a larger universe +and then use ``inject`` to turn it into a ``set : Type u#0``. The +encoding of ``delta_big`` is fairly direct: Its domain type is the +type of sets ``s`` paired with a proof that ``s`` does not contain +itself; and its comprehension function just returns ``s`` itself. + +.. literalinclude:: ../code/Russell.fst + :language: fstar + :start-after: //SNIPPET_START: delta$ + :end-before: //SNIPPET_END: delta$ + +We can now prove both ``delta `mem` delta`` and ``delta `not_mem` +delta``, using the unsound ``inj_proj`` axiom that breaks the universe +system, and derive ``False``. + +.. literalinclude:: ../code/Russell.fst + :language: fstar + :start-after: //SNIPPET_START: proof$ + :end-before: //SNIPPET_END: proof$ + +The proofs are more detailed than they need to be, and if you're +curious, maybe you can follow along by reading the comments. + +The upshot, however, is that without the stratification of universes, +F* would be unsound. + + +Refinement types, FStar.Squash, ``prop``, and Impredicativity +------------------------------------------------------------- + +We've seen how universes levels are computed for arrow types and +inductive type definitions. The other way in which types can be formed +in F* is with refinement types: ``x:t{p}``. As we've seen previously, +a value ``v`` of type ``x:t{p}`` is just a ``v:t`` where ``p[v/x]`` is +derivable in the current scope in F*'s SMT-assisted classical +logic—there is no way to extract a proof of ``p`` from a proof of +``x:t{p}``, i.e., refinement types are F*'s mechanism for proof +irrelevance. + +**Universe of a refinement type**: The universe of a refinement type ``x:t{p}`` is the universe of ``t``. + +Since the universe of a refinement type does not depend on ``p``, it +enables a limited form of impredicativity, and we can define the +following type (summarized here from the F* standard library +``FStar.Squash``): + +.. code-block:: fstar + + let squash (p:Type u#p) : Type u#0 = _:unit { p } + let return_squash (p:Type u#p) (x:p) : squash p = () + +This is a lot like the ``lower`` and ``inject`` assumptions that we +saw in the previous section, but, importantly, there is no ``project`` +operation to invert an ``inject``. In fact, ``FStar.Squash`` proves +that ``squash p`` is proof irrelevant, meaning that all proofs of +``squash p`` are equal. + +.. code-block:: fstar + + val proof_irrelevance (p: Type u#p) (x y: squash p) : squash (x == y) + +``FStar.Squash`` does provide a limited way to manipulate a proof of +``p`` given a ``squash p``, using the combinator ``bind_squash`` shown +below, which states that if ``f`` can build a proof ``squash b`` from any +proof of ``a``, then it can do so from the one and only proof of ``a`` +that is witnessed by ``x:squash a``. + +.. code-block:: fstar + + val bind_squash (#a: Type u#a) (#b: Type u#b) (x: squash a) (f: (a -> squash b)) : squash b + +It is important that ``bind_squash`` return a ``squash b``, +maintaining the proof-irrelevance of the ``squash`` type. Otherwise, +if one could extract a proof of ``a`` from ``squash a``, we would be +perilously close to the unsound ``project`` axiom which enables +paradoxes. + +This restriction is similar to Coq's restriction on its ``Prop`` type, +forbidding functions match on ``Prop`` to return results outside +``Prop``. + +The F* type ``prop`` (which we saw first :ref:`here `) is +defined primitively as type of all squashed types, i.e., the only +types in ``prop`` are types of the form ``squash p``; or, +equivalently, every type ``t : prop``, is a subtype of ``unit``. Being +the type of a class of types, ``prop`` in F* lives in ``u#1`` + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: prop$ + :end-before: //SNIPPET_END: prop$ + +However, ``prop`` still offers a form of impredicativity, e.g., you +can quantify over all ``prop`` while remaining in ``prop``. + +.. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: prop impredicative$ + :end-before: //SNIPPET_END: prop impredicative$ + +* The first line above shows that, as usual, an arrow type is in a + universe that is the maximum of the universes of its argument and + result types. In this case, since it has an argument ``prop : Type + u#1`` the arrow itself is in ``u#1``. + +* The second line shows that by squashing the arrow type, we can bring + it back to ``u#0`` + +* The third line shows the more customary way of doing this in F*, + where ``forall (a:prop). a`` is just syntactic sugar for ``squash + (a:prop -> a)``. Since this is a ``squash`` type, not only does it + live in ``Type u#0``, it is itself a ``prop``. + +* The fourth line shows that the same is true for ``exists``. + +.. _Part2_Universes_raising: + +Raising universes and the lack of cumulativity +---------------------------------------------- + +In some type theories, notably in Coq, the universe system is +*cumulative*, meaning that ``Type u#i : Type u#(max (i + i) j)``; +or, that ``Type u#i`` inhabits all universes greater than +``i``. In contrast, in F*, as in Agda and Lean, ``Type u#i : Type +u#(i + 1)``, i.e., a type resides only in the universe immediately +above it. + +Cumulativity is a form of subtyping on universe levels, and it can be +quite useful, enabling definitions at higher universes to be re-used +for all lower universes. However, systems that mix universe +polymorphism with cumulativity are quite tricky, and indeed, it was +only recently that Coq offered both universe polymorphism and +cumulativity. + +Lacking cumulativity, F* provides a library ``FStar.Universe`` that +enables lifting a term from one universe to a higher one. We summarize +it here: + +.. code-block:: fstar + + val raise_t ([@@@ strictly_positive] t : Type u#a) : Type u#(max a b) + + val raise_val (#a:Type u#a) (x:a) : raise_t u#a u#b a + + val downgrade_val (#a:Type u#a) (x:raise_t u#a u#b a) : a + + val downgrade_val_raise_val (#a: Type u#a) (x: a) + : Lemma (downgrade_val u#a u#b (raise_val x) == x) + + val raise_val_downgrade_val (#a: Type u#a) (x: raise_t u#a u#b a) + : Lemma (raise_val (downgrade_val x) == x) + +The type ``raise_t t`` is strictly positive in ``t`` and raises ``t`` +from ``u#a`` to ``u#(max a b)``. ``raise_val`` and +``downgrade_val`` are mutually inverse functions between ``t`` and +``raise_t t``. + +This signature is similar in structure to the unsound signature for +``lower, inject, project`` that we use to exhibit Russell's +paradox. However, crucially, the universe levels in ``raise_t`` ensure +that the universe levels *increase*, preventing any violation of +universe stratification. + +In fact, this signature is readily implemented in F*, as shown below, +where the universe annotation on ``raise_t`` explicitly defines the +type in a higher universe ``u#(max a b)`` rather than in its minimum +universe ``u#a``. + +.. code-block:: fstar + + noeq + type raise_t (a : Type u#a) : Type u#(max a b) = + | Ret : a -> raise_t a + + let raise_val #a x = Ret x + let downgrade_val #a x = match x with Ret x0 -> x0 + let downgrade_val_raise_val #a x = () + let raise_val_downgrade_val #a x = () + +.. _Part2_tips_for_universes: + +Tips for working with universes +------------------------------- + +Whenever you write ``Type`` in F*, you are implicitly writing ``Type +u#?x``, where ``?x`` is a universe *metavariable* left for F* to infer. When +left implicit, this means that F* may sometimes infer universes for +your definition that are not what you expect---they may be too general +or not general enough. We conclude this section with a few tips to +detect and fix such problems. + +* If you see puzzling error messages, enable the following pragma: + + .. code-block:: fstar + + #push-options "--print_implicits --print_universes" + + This will cause F* to print larger terms in error messages, which + you usually do not want, except when you are confronted with error + messages of the form "expected type t; got type t". + +* Aside from the built-in constants ``Type u#a``, the ``->`` type + constructor, and the refinement type former, the only universe + polymorphic F* terms are top-level definitions. That is, while you + can define ``i`` at the top-level and use it polymorphically: + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: id_top_level$ + :end-before: //SNIPPET_END: id_top_level$ + + You cannot do the same in a non-top-level scope: + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: no_local_poly$ + :end-before: //SNIPPET_END: no_local_poly$ + + Of course, non-universe-polymorphic definitions work at all scopes, + e.g., here, the ``i`` is polymorphic in all types at universe + ``u#0``. + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: type_poly$ + :end-before: //SNIPPET_END: type_poly$ + +* If you write a ``val f : t`` declaration for ``f``, F* will compute + the most general universe for the type ``t`` independently of the + ``let f = e`` or ``type f =`` definition. + + A simple example of this behavior is the following. Say, you declare + ``tup2`` as below. + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: val tup2$ + :end-before: //SNIPPET_END: val tup2$ + + Seeing this declaration F* infers ``val tup2 (a:Type u#a) (b:Type u#b) + : Type u#c``, computing the most general type for ``tup2``. + + If you now try to define ``tup2``, + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: let tup2$ + :end-before: //SNIPPET_END: let tup2$ + + F* complains with the following error (with ``--print_universes`` on): + + .. code-block:: + + Type u#(max uu___43588 uu___43589) is not a subtype of the expected type Type u#uu___43590 + + Meaning that the inferred type for the definition of ``tup2 a b`` is + ``Type u#(max a b)``, which is of course not the same a ``Type + u#c``, and, sadly, the auto-generated fresh names in the error + message don't make your life any easier. + + The reason for this is that one can write a ``val f : t`` in a + context where a definition for ``f`` may never appear, in which case + F* has to compute some universes for ``t``---it chooses the most + general universe, though if you do try to implement ``f`` you may + find that the most general universe is too general. + + A good rule of thumb is the following: + + - Do not write a ``val`` declaration for a term, unless you are + writing an :ref:`interface `. Instead, directly + write a ``let`` or ``type`` definition and annotate it with the + type you expect it to have---this will lead to fewer + surprises. For example, instead of separating the ``val tup2`` + from ``let tup2`` just write them together, as shown below, and F* + infers the correct universes. + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: tuple2$ + :end-before: //SNIPPET_END: tuple2$ + + - If you must write a ``val f : t``, because, say, the type ``t`` is + huge, or because you are writing an interface, it's a good idea to + be explicit about universes, so that when defining ``f``, you know + exactly how general you have to be in terms of universes; and, + conversely, users of ``f`` know exactly how much universe + polymorphism they are getting. For example: + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: tup2_again$ + :end-before: //SNIPPET_END: tup2_again$ + +* When defining an inductive type, prefer using parameters over + indexes, since usually type parameters lead to types in lower + universes. For example, one might think to define lists this way: + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: list_alt$ + :end-before: //SNIPPET_END: list_alt$ + + Although semantically equivalent to the standard list + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: list$ + :end-before: //SNIPPET_END: list$ + + ``list_alt`` produces a type in ``u#(a + 1)``, since both ``NilAlt`` + and ``ConsAlt`` have fields of type ``a:Type u#a``. So, unless the + index of your type varies among the constructors, use a parameter + instead of an index. + + That said, recall that it's the fields of the constructors of the + inductive type that count. You can index your type by a type in any + universe and it doesn't influence the result type. Here's an + artificial example. + + .. literalinclude:: ../code/Universes.fst + :language: fstar + :start-after: //SNIPPET_START: crazy_index$ + :end-before: //SNIPPET_END: crazy_index$ diff --git a/book/part2/part2_vectors.rst b/book/part2/part2_vectors.rst new file mode 100644 index 00000000000..b374ca1d364 --- /dev/null +++ b/book/part2/part2_vectors.rst @@ -0,0 +1,327 @@ +.. _Part2_vectors: + +Length-indexed Lists +==================== + +To make concrete some aspects of the formal definitions above, we'll +look at several variants of a parameterized list datatype augmented +with indexes that carry information about the list's length. + +Even and Odd-lengthed Lists +........................... + +Our first example is bit artifical, but helps illustrate a usage of +mutually inductive types. + +Here, we're defining two types constructors called ``even`` and +``odd``, (i.e, just :math:`T_1` and :math:`T_2` from our formal +definition), both with a single parameter ``(a:Type)``, for the type +of the lists' elements, and no indexes. + +All lists of type ``even a`` have an even number of elements---zero +elements, using its first constructor ``ENil``, or using ``ECons``, +one more than the number of elements in an ``odd a``, a list with an +odd number of elements. Elements of the type ``odd a`` are constructed +using the constructor ``OCons``, which adds a single element to an +``even a`` list. The types are mutually inductive since their +definitions reference each other. + + +.. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: //SNIPPET_START: even_and_odd + :end-before: //SNIPPET_END: even_and_odd + +Although closely related, the types ``even a`` and ``odd a`` are from +distinct inductive types. So, to compute, say, the length of one of +these lists one generally write a pair of mutually recursive +functions, like so: + +.. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: //SNIPPET_START: elength_and_olength + :end-before: //SNIPPET_END: elength_and_olength + +Note, we can prove that the length of an ``even a`` and ``odd a`` are +really even and odd. + +Now, say, you wanted to map a function over an ``even a``, you'd have +to write a pair of mutually recursive functions to map simultaneoulsy +over them both. This can get tedious quickly. Instead of rolling out +several mutually inductive but distinct types, one can instead use an +*indexed* type to group related types in the same inductive family of +types. + +The definition of ``even_or_odd_list`` below shows an inductive type +with one parameter ``a``, for the type of lists elements, and a single +boolean index, which indicates whether the list is even or odd. Note +how the index varies in the types of the constructors, whereas the +parameter stays the same in all instances. + +.. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: //SNIPPET_START: even_or_odd_list + :end-before: //SNIPPET_END: even_or_odd_list + +Now, we have a single family of types for both even and odd lists, and +we can write a single function that abstracts over both even and odd +lists, just by abstracting over the boolean index. For example, +``eo_length`` computes the length of an ``even_or_odd_list``, with its +type showing that it returns an even number with ``b`` is true and an +odd number otherwise. + +.. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: //SNIPPET_START: eo_length + :end-before: //SNIPPET_END: eo_length + +.. note:: + + Note, in ``eo_length`` we had to explicitly specify a decreases + clause to prove the function terminating. Why? Refer back to the + section on :ref:`default + measures` to recall that by + default is the lexicographic ordering of all the arguments in + order. So, without the decreases clause, F* will try to prove that + the index argument ``b`` decreases on the recursive call, which it + does not. + +This is our first type with with both parameters and indices. But why +stop at just indexing to distinguish even and odd-lengthed lists? We +can index a list by its length itself. + +Vectors +....... + +Let's look again at the definition of the ``vec`` type, first shown in +:ref:`the introduction`. + +.. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: //SNIPPET_START: vec + :end-before: //SNIPPET_END: vec + +Here, we're defining just a single type constructor called ``vec`` +(i.e, just :math:`T_1`), which a single parameter ``(a:Type)`` and one +index ``nat``. + +``vec`` has two data constructors: ``Nil`` builds an instance of ``vec +a 0``, the empty vector; and ``Cons hd tl`` builds an instance of +``vec a (n + 1)`` from a head element ``hd:a`` and a tail ``tl : vec a +n``. That is, the two constructors build different instances of +``vec``---those instances have the same parameter (``a``), but +different indexes (``0`` and ``n + 1``). + +.. note:: + + Datatypes in many languages in the ML family, including OCaml and + F#, have parameters but no indexes. So, all the data construtors + construct the same instance of the type constructor. Further, all + data constructors take at most one argument. If your datatype + happens to be simple enough to fit these restrictions, you can use + a notation similar to OCaml or F* for those types in F*. For + example, here's the ``option`` type defined in F* using an + OCaml-like notation. + + .. code-block:: fstar + + type option a = + | None + | Some of a + + This is equivalent to + + .. code-block:: fstar + + type option a = + | None : option a + | Some : a -> option a + +Getting an element from a vector +................................ + +With our length-indexed ``vec`` type, one can write functions with +types that make use of the length information to ensure that they are +well-defined. For example, to get the ``i`` th element of a vector, one +can write: + +.. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: //SNIPPET_START: long_get + :end-before: //SNIPPET_END: long_get + +The type of ``get i v`` says that ``i`` must be less than ``n``, where +``n`` is the length of ``v``, i.e., that ``i`` is within bounds of the +vector, which is enough to prove that ``get i v`` can always return an +element of type ``a``. Let's look a bit more closely at how this +function is typechecked by F*. + +The first key bit is pattern matching ``v``: + +.. code-block:: fstar + + match v with + | Nil -> false_elim() + | Cons hd tl -> + +In case ``v`` is ``Nil``, we use the library function +``Prims.false_elim : squash False -> a`` to express that this case is +impossible. Intuitively, since the index ``i`` is a natural number +strictly less than the length of the list, we should be able to +convince F* that ``n <> 0``. + +The way this works is that F* typechecks the branch in a context that +includes an *equation*, namely that the ``v : vec a n`` equals the +pattern ``Nil : vec a 0``. With the assumption that ``v == Nil`` in +the context, F* tries to check that ``false_elim`` is well-typed, +which in turn requires ``() : squash False``. This produces an proof +obligation sent to the SMT solver, which is able to prove ``False`` in +this case, since from ``v = Nil`` we must have that ``n = 0`` which +contradicts ``i < n``. Put another way, the branch where ``v = Nil`` +is unreachable given the precondition ``i < n``. + +.. note:: + + When a branch is unreachable, F* allows you to just omit the branch + altogether, rather than writing it an explicitly calling + ``false_elim``. For example, it is more common to write: + + .. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: //SNIPPET_START: get + :end-before: //SNIPPET_END: get + + where ``let Cons hd tl = v`` pattern matches ``v`` against just + ``Cons hd tl``. F* automatically proves that the other cases of + the match are unreachable. + +Now, turning to the second case, we have a pattern like this: + +.. code-block:: fstar + + match v with + | Cons hd tl -> + +But, recall that ``Cons`` has an implicit first argument describing +the length of ``tl``. So, more explicitly, our pattern is of the form +below, where ``tl : vec a m``. + +.. code-block:: fstar + + match v with + | Cons #m hd tl -> + +F* typechecks the branch in a context that includes the equation that +``v == Cons #m hd tl``, which lets the solve conclude that ``n == m + +1``, from the type of ``Cons``. + +If ``i=0``, we've found the element we want and return it. + +Otherwise, we make a recursive call ``get (i - 1) tl`` and now F* has +to: + + * Instantiate the implicit argument of ``get`` to ``m``, the length + of ``tl``. That is, in explicit form, this recursive call is + really ``get #m (i - 1) tl``. F* does this by relying on a + unification algorithm implemented as part of its type inference + procedure. + + * Prove that ``(i - 1) < m``, which follows from ``i < n`` and ``n + == m + 1``. + + * Prove that the recursive call terminates, by proving that ``m << + n``, or, equivalently, since ``m`` and ``n`` are natural numbers, + ``m < n``. This is easy, since we have ``n == m + 1``. + +Let's try a few exercises. The main work is to find a type for the +functions in question. Once you do, the rest of the code will "write +itself". + +Exercises +......... + + +Exercise: Concatenating vectors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`Click here <../code/exercises/Part2.Vec.fst>`_ for the exercise file. + +Implement a function to concatenate vectors. It should have the +following signature: + +.. code-block:: fstar + + val append (#a:Type) (#n #m:nat) (v1:vec a n) (v2:vec a m) + : vec a (n + m) + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: append + :end-before: SNIPPET_END: append + +-------------------------------------------------------------------------------- + +Exercise: Splitting a vector +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Write a function called ``split_at`` to split a vector ``v : vec a n`` +at index ``i`` into its ``i`` -length prefix from position ``0`` and a +suffix starting at ``i``. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: split_at + :end-before: SNIPPET_END: split_at + +-------------------------------------------------------------------------------- + +Write a tail-recursive version of ``split_at``. You will need a +``reverse`` function as a helper + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: reverse + :end-before: SNIPPET_END: reverse + + .. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: SNIPPET_START: split_at_tail + :end-before: SNIPPET_END: split_at_tail + +Bonus: Prove ``split_at`` and ``split_at_tail`` are equivalent. + +-------------------------------------------------------------------------------- + +Vectors: Probably not worth it +.............................. + +Many texts about dependent types showcase length-indexed vectors, much +as we've done here. Although useful as a simple illustrative example, +the ``vec`` type we've seen is probably not what you want to use in +practice. Especially in F*, where regular lists can easily be used +with refinement types, length-indexed vectors are redundant. The code +below shows how: + + .. literalinclude:: ../code/LList.fst + :language: fstar + +In the next few sections, we'll see more useful examples of indexed +inductive types than just mere length-indexed vectors. diff --git a/book/part2/part2_well_founded.rst b/book/part2/part2_well_founded.rst new file mode 100644 index 00000000000..c01b87789f6 --- /dev/null +++ b/book/part2/part2_well_founded.rst @@ -0,0 +1,243 @@ +.. _Part2_well_founded_recursion: + +Well-founded Relations and Termination +====================================== + +In an earlier chapter on :ref:`proofs of termination +`, we learned about how F* checks that recursive +functions terminate. In this chapter, we see how the termination check +arises from inductive types and structural recursion. Just as with +:ref:`equality `, termination checking, a core feature +of F*'s logic and proof system, finds its foundation in inductive +types. + +For more technical background on this topic, the following resources +may be useful: + + * `Constructing Recursion Operators in Type Theory, L. Paulson, Journal of Symbolic Computation (1986) 2, 325-355 `_ + + * `Modeling General Recursion in Type Theory, A. Bove & V. Capretta, + Mathematical Structures in Computer Science (2005) + `_ + +Thanks to Aseem Rastogi, Chantal Keller, and Catalin Hritcu, for +providing some of the F* libraries presented in this chapter. + + * `FStar.WellFounded.fst `_ + + * `FStar.LexicographicOrdering `_ + + + +Well-founded Relations and Accessibility Predicates +--------------------------------------------------- + +A binary relation :math:`R` on elements of type :math:`T` is +well-founded if there is no infinite sequence :math:`x_0, x_1, x_2, +...`, such that :math:`x_i~R~x_{i + 1}`, for all :math:`i`. + +As explained :ref:`earlier `, when typechecking a +recursive function ``f``, F* requires the user to provide a *measure*, +some function of the arguments of `f`, and checks that on a recursive +call, the measure of the arguments is related to the measure of the +formal parameters a built-in well-founded relation on F* terms. Since +well-founded relations have no infinite descending chains, every chain +of recursive calls related by such a relation must eventually +terminate. However, this built-in well-founded relation, written +``<<`` or ``precedes``, is a derived notion. + +In its most primitive form, the well-foundedness of a relation can be +expressed in terms of an inductive type ``acc`` (short for +"accessible") shown below. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: acc$ + :end-before: //SNIPPET_END: acc$ + +The type ``acc`` is parameterized by a type ``a``; a binary relation +``r: a -> a -> Type`` on ``a``; and an element of the type +``x:a``. Informally, the relation ``r y x`` is provable when ``y`` +is "smaller" than ``x``. + +The ``acc`` type has just one constructor ``AccIntro``, whose only +argument is a function of type ``y:a -> r y x -> acc r +y``. Intuitively, this says that in order to build an instance of +``acc r x0``, you have to provide a function which can build a proof +of ``acc r x1`` for all ``x1:a`` smaller than ``x0``. The only way to +build such a function is one can avoid infinite regress, is if +thechain ``x0 r x1 r x2 r ...``, eventually terminates in some ``xn`` +such that there are no elements smaller than it according to ``r``. + +In other words, if one can prove ``acc r x`` for all ``x:a``, then +this precisely captures the condition that there are no infinite +descending ``r``-related chains in ``a``, or that ``r`` is +well-founded. This is exactly what the definition below says, where +``is_well_founded`` is a classical (SMT-automatable) variant of +``well_founded``. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: well_founded$ + :end-before: //SNIPPET_END: well_founded$ + +Well-founded Recursion +---------------------- + +Given a relation ``r`` and proof of ``p:acc r x`` , one can define a +recursive function on ``x`` whose termination can be established +purely in terms of structural recursion on the proof ``p``, even +though the function may not itself be structurally recursive on ``x``. + +The combinator ``fix_F`` shown below illustrates this at work: + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: fix_F$ + :end-before: //SNIPPET_END: fix_F$ + +If ``f`` is a function such that every recursive call in the +definition of ``f x`` is on an argument ``y``, such that that ``y`` is +smaller than ``x`` according to some relation ``r``; and if starting +from some argument ``x0``, we have a proof of accessibility ``acc r +x0`` (i.e., no infinite descending ``r``-chains starting at ``x0``), +then the fixpoint of ``f`` can be defined by structural recursion on +the proof of ``accessible_x0``. + + * ``fix_F`` is structurally recursive on ``accessible_x0`` since the + recursive call is on an element ``h1 y r_yx``, i.e., a child node + of the (possibly infinitely branching) tree rooted at ``AccIntro h1``. + +A slightly simpler version of ``fix_F`` is derivable if ``r`` is +well-founded, i.e., ``r`` is accessible for all elements ``x:a``. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: fix$ + :end-before: //SNIPPET_END: fix$ + +Some Well-founded Relations +--------------------------- + +We show how to buid some basic well-founded relations here. For +starters, since F* already internalizes that the ``<`` ordering on +natural numbers as part of its termination check, it is easy to prove +that ``<`` is well-founded. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: lt_nat$ + :end-before: //SNIPPET_END: lt_nat$ + +We can also define combinators to derive well-founded relations from +other well-founded relations. For example, if a relation ``sub_r`` is +a *sub-relation* of a well-founded relation ``r`` (meaning we have ``r +x y`` whenever we have ``sub_r x y``), then ``sub_r`` is well-founded +too. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: subrel_wf$ + :end-before: //SNIPPET_END: subrel_wf$ + +Another useful combinator derives the well-foundedness of a relation +``r: binrel b`` if it can be defined as the inverse image under some +function ``f: a -> b`` of some other well-founded relation ``r: +binrel``. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: inverse_image$ + :end-before: //SNIPPET_END: inverse_image$ + +For example, the ``>`` ordering on negative numbers can be proven +well-founded by defining it as the inverse image of the ``<`` ordering +on natural numbers. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: inverse_image_neg$ + :end-before: //SNIPPET_END: inverse_image_neg$ + +Termination Checking with Custom Well-founded Relations +------------------------------------------------------- + +In the F* library, ``FStar.LexicographicOrdering``, several other +relations are proven to be well-founded, including the lexicographic +ordering on dependent pairs. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: lexicographic_order$ + :end-before: //SNIPPET_END: lexicographic_order$ + +This order, defined as a ``binrel (x:a & b x)``, and is paramterized +by a binary relation (``r_a``) on ``a`` and a family of relations +(``r_b``) on ``b x``, one for each ``x:a``. It has two cases: + + * ``Left_lex``: The first component of the pair decreases by + ``r_a``, and the second component is irrelevant. + + + * ``Right_lex``: The first component of the pair is invariant, but + the second component decreases by ``r_b``. + +The proof is a little involved (see +``FStar.LexicographicOrdering.fst``), but one can prove that it is +well-founded when ``r_a`` and ``r_b`` are themselves well-founded, +i.e., + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: lexicographic_order_wf$ + :end-before: //SNIPPET_END: lexicographic_order_wf$ + +But, with this well-foundedness proof in hand, we can define recursive +functions with our own well-founded orders. + +To illustrate, let's define the ``ackermann`` function again (we saw +it first :ref:`here `), this time using +accessibilty and well-founded relations, rather than the built-in +``precedes`` relation. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: ackermann_manual$ + :end-before: //SNIPPET_END: ackermann_manual$ + +This version is way more verbose than the ackermann we saw +earlier---but this version demonstrates that F*'s built-in support for +the lexicographic orders over the precedes relation is semantically +justified by a more primitive model of well-founded relations + +To make user-defined well-founded orderings easier to work with, F* +actually provides a variant of the ``decreases`` clause to work with +well-founded relations. For example, one can use the following syntax +to gain from F*'s built-in from SMT automation and termination +checking, with the expressiveness of using ones own well-founded relation. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: ackermann_wf$ + :end-before: //SNIPPET_END: ackermann_wf$ + +To explain the syntax: + + * ``decreases {:well-founded p x}``: Here, ``p`` is meant to be an + instance for + ``FStar.LexicographicOrdering.well_founded_relation``, applied to + some term ``x`` built from the formal parameters in scope. + + * In this case, we use the combinator ``L.lex`` to build a + lexicographic ordering from ``wf_lt_nat`` (coercing it using a + utility ``coerce_wf`` to turn the definitions used in our tutorial + chapter here to the types expected by the + ``FStar.LexicographicOrdering`` library). + +We show the coercions below for completeness, though one would not +necessarily need them outside the context of this tutorial. + +.. literalinclude:: ../code/Part2.WellFounded.fst + :language: fstar + :start-after: //SNIPPET_START: coercions$ + :end-before: //SNIPPET_END: coercions$ diff --git a/book/part3/part3.rst b/book/part3/part3.rst new file mode 100644 index 00000000000..f20134ef009 --- /dev/null +++ b/book/part3/part3.rst @@ -0,0 +1,57 @@ +.. _Part3: + +########################################## +Modularity With Interfaces and Typeclasses +########################################## + +In this section, we'll learn about two abstraction techniques used to +structure larger F* developments: *interfaces* and *typeclasses*. + +**Interfaces**: An F* module ``M`` (in a file ``M.fst``) can be paired +with an interface (in a file ``M.fsti``). A module's interface is a +subset of the module's declarations and definitions. Another module +``Client`` that uses ``M`` can only make use of the part of ``M`` +revealed in its interface---the rest of ``M`` is invisible to +``Client``. As such, interfaces provide an abstraction mechanism, +enabling the development of ``Client`` to be independent of any +interface-respecting implementation of ``M``. + +Unlike module systems in other ML-like languages (which provide more +advanced features like signatures, functors, and first-class modules), +F*'s module system is relatively simple. + +* A module can have at most one interface. + +* An interface can have at most one implementation. + +* A module lacking an interface reveals all its details to clients. + +* An interface lacking an implementation can be seen as an assumption or an axiom. + +**Typeclasses**: Typeclasses cater to more complex abstraction +patterns, e.g., where an interface may have several +implementations. Many other languages, ranging from Haskell to Rust, +support typeclasses that are similar in spirit to what F* also +provides. + +Typeclasses in F* are actually defined mostly by a "user-space" +metaprogam (relying on general support for metaprogramming in `Meta-F* +`_), making them very +flexible (e.g., multi-parameter classes, overlapping instances, +etc. are easily supported). + +That said, typeclasses are a relatively recent addition to the +language and most of F*'s standard library does not yet use +typeclasses. As such, they are somewhat less mature than interfaces +and some features require encodings (e.g., typeclass inheritance), +rather than being supported with built-in syntax. + +Thanks especially to Guido Martinez, who designed and implemented +most of F*'s typeclass system. + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + part3_interfaces + part3_typeclasses diff --git a/book/part3/part3_interfaces.rst b/book/part3/part3_interfaces.rst new file mode 100644 index 00000000000..3da407607df --- /dev/null +++ b/book/part3/part3_interfaces.rst @@ -0,0 +1,277 @@ +.. _Part3_interfaces: + +Interfaces +========== + +Look through the F* standard libary (in the ``ulib`` folder) and +you'll find many files with ``.fsti`` extensions. Each of these is an +interface file that pairs with a module implementation in a +corresponding ``.fst`` file. + +An interface (``.fsti``) is very similar to a module implementation +(``.fst``): it can contain all the elements that a module can, +including inductive ``type`` definitions; ``let`` and ``let rec`` +definitions; ``val`` declarations; etc. However, unlike module +implementations, an interface is allowed to declare a symbol ``val f : +t`` without any corresponding definition of ``f``. This makes ``f`` +abstract to the rest of the interface and all client modules, i.e., +``f`` is simply assumed to have type ``t`` without any definition. The +definition of ``f`` is provided in the ``.fst`` file and checked to +have type ``t``, ensuring that a client module's assumption of ``f:t`` +is backed by a suitable definition. + +To see how interfaces work, we'll look at the design of the **bounded +integer** modules ``FStar.UInt32``, ``FStar.UInt64``, and the like, +building our own simplified versions by way of illustration. + +Bounded Integers +^^^^^^^^^^^^^^^^ + +The F* primitive type ``int`` is an unbounded mathematical +integer. When compiling a program to, say, OCaml, ``int`` is compiled +to a "big integer", implemented by OCaml's `ZArith package +`_. However, the ``int`` type +can be inefficient and in some scenarios (e.g., when compiling F* +to C) one may want to work with bounded integers that can always be +represented in machine word. ``FStar.UInt32.t`` and ``FStar.UInt64.t`` +are types from the standard library whose values can always be +represented as 32- and 64-bit unsigned integers, respectively. + +Arithmetic operations on 32-bit unsigned integers (like addition) are +interpreted modulo ``2^32``. However, for many applications, one wants +to program in a discipline that ensures that there is no unintentional +arithmetic overflow, i.e., we'd like to use bounded integers for +efficiency, and by proving that their operations don't overflow we can +reason bounded integer terms without using modular arithmetic. + +.. note:: + + Although we don't discuss them here, F*'s libraries also provide + signed integer types that can be compiled to the corresponding + signed integters in C. Avoiding overflow on signed integer + arithmetic is not just a matter of ease of reasoning, since signed + integer overflow is undefined behavior in C. + +Interface: UInt32.fsti +---------------------- + +The interface ``UInt32`` begins like any module with the +module's name. Although this could be inferred from the name of the file +(``UInt32.fsti``, in this case), F* requires the name to be explicit. + +.. literalinclude:: ../code/UInt32.fsti + :language: fstar + :start-after: //SNIPPET_START: t$ + :end-before: //SNIPPET_END: t$ + +``UInt32`` provides one abstract type ``val t : eqtype``, the type of +our bounded integer. Its type says that it supports decidable +equality, but no definition of ``t`` is revaled in the interface. + +The operations on ``t`` are specified in terms of a logical model that +relates ``t`` to bounded mathematical integers, in particular +``u32_nat``, a natural number less than ``pow2 32``. + +.. literalinclude:: ../code/UInt32.fsti + :language: fstar + :start-after: //SNIPPET_START: bounds$ + :end-before: //SNIPPET_END: bounds$ + +.. note:: + + Unlike interfaces in languages like OCaml, interfaces in F* *can* + include ``let`` and ``let rec`` definitions. As we see in + ``UInt32``, these definitions are often useful for giving precise + specifications to the other operations whose signatures appear in + the interface. + +To relate our abstract type ``t`` to ``u32_nat``, the interface +provides two coercions ``v`` and ``u`` that go back and forth between +``t`` and ``u32_nat``. The lemma signatures ``vu_inv`` and +``uv_inv`` require ``v`` and ``u`` to be mutually inverse, meaning +that ``t`` and ``u32_nat`` are in 1-1 correspondence. + +.. literalinclude:: ../code/UInt32.fsti + :language: fstar + :start-after: //SNIPPET_START: vu$ + :end-before: //SNIPPET_END: vu$ + +Modular addition and subtraction +................................ + +Addition and subtraction on ``t`` values are defined modulo ``pow2 +32``. This is specified by the signatures of ``add_mod`` and +``sub_mod`` below. + +.. literalinclude:: ../code/UInt32.fsti + :language: fstar + :start-after: //SNIPPET_START: add_mod$ + :end-before: //SNIPPET_END: add_mod$ + +.. literalinclude:: ../code/UInt32.fsti + :language: fstar + :start-after: //SNIPPET_START: sub_mod$ + :end-before: //SNIPPET_END: sub_mod$ + +Bounds-checked addition and subtraction +....................................... + +Although precise, the types of ``add_mod`` and ``sub_mod`` aren't +always easy to reason with. For example, proving that ``add_mod (u 2) +(u 3) == u 5`` requires reasoning about modular arithmetic---for +constants like ``2``, ``3``, and ``5`` this is easy enough, but proofs +about modular arithmetic over symbolic values will, in general, involve +reasoning about non-linear arithmetic, which is difficult to automate +even with an SMT solver. Besides, in many safety critical software +systems, one often prefers to avoid integer overflow altogether. + +So, the ``UInt32`` interface also provides two additional operations, +``add`` and ``sub``, whose specification enables two ``t`` values to be added +(resp. subtracted) only if there is no overflow (or underflow). + +First, we define an auxiliary predicate ``fits`` to state when an +operation does not overflow or underflow. + +.. literalinclude:: ../code/UInt32.fsti + :language: fstar + :start-after: //SNIPPET_START: fits$ + :end-before: //SNIPPET_END: fits$ + +Then, we use ``fits`` to restrict the domain of ``add`` and ``sub`` +and the type ensures that the result is the sum (resp. difference) of +the arguments, without need for any modular arithmetic. + +.. literalinclude:: ../code/UInt32.fsti + :language: fstar + :start-after: //SNIPPET_START: add$ + :end-before: //SNIPPET_END: add$ + +.. literalinclude:: ../code/UInt32.fsti + :language: fstar + :start-after: //SNIPPET_START: sub$ + :end-before: //SNIPPET_END: sub$ + +.. note :: + + Although the addition operator can be used as a first-class + function with the notation ``(+)``, the same does not work for + subtraction, since ``(-)`` resolves to unary integer negation, + rather than subtraction---so, we write ``fun x y -> x - y``. + +Comparison +.......... + +Finally, the interface also provides a comparison operator ``lt``, as +specified below: + +.. literalinclude:: ../code/UInt32.fsti + :language: fstar + :start-after: //SNIPPET_START: lt$ + :end-before: //SNIPPET_END: lt$ + +Implementation: UInt32.fst +-------------------------- + +An implementation of ``UInt32`` must provide definitions for +all the ``val`` declarations in the ``UInt32`` interface, starting +with a representation for the abstract type ``t``. + +There are multiple possible representations for ``t``, however the +point of the interface is to isolate client modules from these +implementation choices. + +Perhaps the easiest implementation is to represent ``t`` as a +``u32_nat`` itself. This makes proving the correspondence between +``t`` and its logical model almost trivial. + +.. literalinclude:: ../code/UInt32.fst + :language: fstar + +Another choice may be to represent ``t`` as a 32-bit vector. This is a +bit harder and proving that it is correct with respect to the +interface requires handling some interactions between Z3's theory of +bit vectors and uninterpreted functions, which we handle with a +tactic. This is quite advanced, and we have yet to cover F*'s support +for tactics, but we show the code below for reference. + +.. literalinclude:: ../code/UInt32BV.fst + :language: fstar + :start-after: //SNIPPET_START: UInt32BV$ + :end-before: //SNIPPET_END: UInt32BV$ + +Although both implementations correctly satisfy the ``UInt32`` +interface, F* requires the user to pick one. Unlike module systems in some +other ML-like languages, where interfaces are first-class entities +which many modules can implement, in F* an interface can have at most +one implementation. For interfaces with multiple implementations, one +must use typeclasses. + + +Interleaving: A Quirk +--------------------- + +The current F* implementation views an interface and its +implementation as two partially implemented halves of a module. When +checking that an implementation is a correct implementation of an +interface, F* attempts to combine the two halves into a complete +module before typechecking it. It does this by trying to *interleave* +the top-level elements of the interface and implementation, preserving +their relative order. + +This implementation strategy is far from optimal in various ways and a +relic from a time when F*'s implementation did not support separate +compilation. This implementation strategy is likely to change in the +future (see `this issue +`_ for +details). + +Meanwhile, the main thing to keep in mind when implementing interfaces +is the following: + + * The order of definitions in an implementation much match the order + of ``val`` declarations in the interface. E.g., if the interface + contains ``val f : tf`` followed by ``val g : tg``, then the + implementation of ``f`` must precede the implementation of ``g``. + +Also, remember that if you are writing ``val`` declarations in an +interface, it is a good idea to be explicit about universe levels. See +:ref:`here for more discussion `. + +Other issues with interleaving that may help in debugging compiler +errors with interfaces: + + * `Issue 2020 `_ + * `Issue 1770 `_ + * `Issue 959 `_ + +Comparison with machine integers in the F* library +-------------------------------------------------- + +F*'s standard library includes ``FStar.UInt32``, whose interface is +similar, though more extensive than the ``UInt32`` shown in this +chapter. For example, ``FStar.UInt32`` also includes multiplication, +division, modulus, bitwise logical operations, etc. + +The implementation of ``FStar.UInt32`` chooses a representation for +``FStar.UInt32.t`` that is similar to ``u32_nat``, though the F* +compiler has special knowledge of this module and treats +``FStar.UInt32.t`` as a primitive type and compiles it and its +operations in a platform-specific way to machine integers. The +implementation of ``FStar.UInt32`` serves only to prove that its +interface is logically consistent by providing a model in terms of +bounded natural numbers. + +The library also provides several other unsigned machine integer types +in addition to ``FStar.UInt32``, including ``FStar.UInt8``, +``FStar.UInt16``, and ``FStar.UInt64``. F* also has several signed +machine integer types. + +All of these modules are very similar, but not being first-class +entities in the language, there is no way to define a general +interface that is instantiated by all these modules. In fact, all +these variants are generated by a script from a common template. + +Although interfaces are well-suited to simple patterns of information +hiding and modular structure, as we'll learn next, typeclasses are +more powerful and enable more generic solutions, though sometimes +requiring the use of higher-order code. diff --git a/book/part3/part3_typeclasses.rst b/book/part3/part3_typeclasses.rst new file mode 100644 index 00000000000..6442fe017d2 --- /dev/null +++ b/book/part3/part3_typeclasses.rst @@ -0,0 +1,685 @@ +.. _Part3_typeclasses: + +Typeclasses +=========== + +Consider writing a program using bounded unsigned integers while being +generic in the actual bounded integer type, E.g., a function that sums +a list of bounded integers while checking for overflow, applicable to +both ``UInt32`` and ``UInt64``. Since F*'s interfaces are not +first-class, one can't easily write a program that abstracts over +those interfaces. Typeclasses can help. + +Some background reading on typeclasses: + + * Phil Wadler and Stephen Blott introduced the idea in 1989 in a + paper titled "`How to make ad hoc polymorphism less ad hoc + `_." Their work, with + many extensions over the years, is the basis of Haskell's + typeclasses. + + * A tutorial on typeclasses in the Coq proof assistant is available + `here + `_. + + * Typeclasses are used heavily in the Lean proof assistant to + structure its `math library + `_. + +Printable +--------- + +A typeclass associates a set of *methods* to a tuple of types, +corresponding to the operations that can be performed using those +types. + +For instance, some types may support an operation that enables them to +be printed as strings. A typeclass ``printable (a:Type)`` represent +the class of all types that support a ``to_string : a -> string`` +operation. + +.. literalinclude:: ../code/Typeclasses.fst + :language: fstar + :start-after: //SNIPPET_START: printable$ + :end-before: //SNIPPET_END: printable$ + +The keyword ``class`` introduces a new typeclass, defined as a +:ref:`record type ` with each method represented as a +field of the record. + +To define instances of a typeclass, one uses the ``instance`` keyword, +as shown below. + +.. literalinclude:: ../code/Typeclasses.fst + :language: fstar + :start-after: //SNIPPET_START: printable_bool_and_int$ + :end-before: //SNIPPET_END: printable_bool_and_int$ + +The notation ``instance printable_bool : printable bool = e`` states +that the value ``e`` is a record value of type ``printable bool``, and +just as with a ``let``-binding, the term ``e`` is bound to the +top-level name ``printable_bool``. + +The convenience of typeclasses is that having defined a class, the +typeclass method is automatically overloaded for all instances of the +class, and the type inference algorithm finds the suitable instance to +use. This is the original motivation of typeclasses---to provide a +principled approach to operator overloading. + +For instance, we can now write ``printb`` and ``printi`` and use +``to_string`` to print both booleans and integers, since we shown that +they are instances of the class ``printable``. + +.. literalinclude:: ../code/Typeclasses.fst + :language: fstar + :start-after: //SNIPPET_START: printb and printi$ + :end-before: //SNIPPET_END: printb and printi$ + +Instances need not be only for base types. For example, all lists are +printable so long as their elements are, and this is captured by what +follows. + +.. literalinclude:: ../code/Typeclasses.fst + :language: fstar + :start-after: //SNIPPET_START: printable_list$ + :end-before: //SNIPPET_END: printable_list$ + +That is, ``printable_list`` constructs a ``to_string`` method of type +``list a -> string`` by mapping the ``to_string`` method of the +``printable a`` instance over the list. And now we can use +``to_string`` with lists of booleans and integers too. + +.. literalinclude:: ../code/Typeclasses.fst + :language: fstar + :start-after: //SNIPPET_START: printis and printbs$ + :end-before: //SNIPPET_END: printis and printbs$ + +There's nothing particularly specific about the ground instances +``printable bool`` and ``printable int``. It's possible to write +programs that are polymorphic in printable types. For example, here's +a function ``print_any_list`` that is explicitly parameterized by a +``printable a``---one can call it by passing in the instance that one +wishes to use explicitly: + +.. literalinclude:: ../code/Typeclasses.fst + :language: fstar + :start-after: //SNIPPET_START: print_any_list_explicit$ + :end-before: //SNIPPET_END: print_any_list_explicit$ + +However, we can do better and have the compiler figure out which +instance we intend to use by using a bit of special syntax for a +typeclass parameter, as shown below. + +.. literalinclude:: ../code/Typeclasses.fst + :language: fstar + :start-after: //SNIPPET_START: print_any_list$ + :end-before: //SNIPPET_END: print_any_list$ + +The parameter ``{| _ : printable a |}`` indicates an implicit argument +that, at each call site, is to be computed by the compiler by finding +a suitable typeclass instance derivable from the instances in +scope. In the first example above, F* figures out that the instance +needed is ``printable_list printable_int : printable (list +int)``. Note, you can always pass the typeclass instance you want +explicitly, if you really want to, as the second example ``_ex2`` +above shows. + +In many cases, the implicit typeclass argument need not be named, in +which case one can just omit the name and write: + +.. literalinclude:: ../code/Typeclasses.fst + :language: fstar + :start-after: //SNIPPET_START: print_any_list_alt$ + :end-before: //SNIPPET_END: print_any_list_alt$ + +Under the hood +.............. + +When defining a ``class``, F* automatically generates generic +functions corresponding to the methods of the class. For instance, in +the case of ``printable``, F* generates: + +.. code-block:: fstar + + let to_string #a {| i : printable a |} (x:a) = i.to_string x + +Having this in scope overloads ``to_string`` for all instance of the +``printable`` class. In the implementation of ``to_string``, we use +the instance ``i`` (just a record, sometimes called a dictionary in +the typeclass literature) and project its ``to_string`` field and +apply it to ``x``. + +Defining an ``instance p x1..xn : t = e`` is just +like an ordinary let binding ``let p x1..xn : t = e``, however the +``instance`` keyword instructs F*'s type inference algorithm to +consider using ``p`` when trying to instantiate implicit arguments +for typeclass instances. + +For example, at the call site ``to_string (x:bool)``, having unified +the implicit type arguments ``a`` with ``bool``, what remains is to +find an instance of ``printable bool``. F* looks through the current +context for all variable bindings in the local scope, and ``instance`` +declarations in the top-level scope, for a instance of ``printable +bool``, taking the first one it is able to construct. + +The resolution procedure for ``to_string [[1;2;3]]`` is a bit more +interesting, since we need to find an instance ``printable (list +int)``, although no such ground instance exists. However, the +typeclass resolution procedure finds the ``printable_list`` instance +function, whose result type ``printable (list a)`` matches the goal +``printable (list int)``, provided ``a = int``. The resolution +procedure then spawns a sub-goal ``printable int``, which it solves +easily and completes the derivation of ``printable (list int)``. + +This backwards, goal-directed search for typeclass resolution is a +kind of logic programming. An interesting implementation detail is +that most of the typeclass machinery is defined as a metaprogran in +``FStar.Tactics.Typeclasses``, outside of the core of F*'s +compiler. As such, the behavior of typeclass resolution is entirely +user-customizable, simply by revising the metaprogram in use. Some +details about how this works can be found in a paper on `Meta F* +`_. + +Exercises +......... + +Define instances of ``printable`` for ``string``, ``a & b``, ``option +a``, and ``either a b``. Check that you can write ``to_string [Inl (0, +1); Inr (Inl (Some true)); Inr (Inr "hello") ]`` and have F* infer the +typeclass instance needed. + +Also write the typeclass instances you need explicitly, just to check +that you understand how things work. This is exercise should also +convey that typeclasses do not increase the expressive power in any +way---whatever is expressible with typeclasses, is also expressible by +explicitly passing records that contain the operations needed on +specific type parameters. However, expliciting passing this operations +can quickly become overwhelming---typeclass inference keeps this +complexity in check and makes it possible to build programs in an +generic, abstract style without too much pain. + +This `exercise file <../code/exercises/Part3.Typeclasses.fst>`_ provides +the definitions you need. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Typeclasses.fst + :language: fstar + :start-after: //SNIPPET_START: print_answer$ + :end-before: //SNIPPET_END: print_answer$ + +-------------------------------------------------------------------------------- + +Bounded Unsigned Integers +------------------------- + +The ``printable`` typeclass is fairly standard and can be defined in +almost any language that supports typeclasses. We now turn to a +typeclass that leverages F*'s dependent types by generalizing the +interface of bounded unsigned integers that we developed in a +:ref:`previous chapter `. + +A type ``a`` is in the class ``bounded_unsigned_int``, when it +admits: + + * An element ``bound : a``, representing the maximum value + + * A pair of functions ``from_nat`` and ``to_nat`` that form a + bijection between ``a`` and natural numbers less than ``to_nat + bound`` + +This is captured by the ``class`` below: + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: bounded_unsigned_int$ + :end-before: //SNIPPET_END: bounded_unsigned_int$ + +.. note :: + + The attribute ``FStar.Tactics.Typeclasses.no_method`` on the + ``properties`` field instructs F* to not generate a typeclass + method for this field. This is useful here, since we don't really + want to overload the name ``properties`` as an operator over all + instances bound the class. It's often convenient to simply ``open + FStar.Tactics.Typeclasses`` when using typeclasses, or to use a + module abbreviation like ``module TC = FStar.Tactics.Typeclasses`` + so that you don't have to use a fully qualified name for + ``no_method``. + +For all ``bounded_unsigned_ints``, one can define a generic ``fits`` +predicate, corresponding to the bounds check condition that we +introduced in the ``UInt32`` interface. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: fits$ + :end-before: //SNIPPET_END: fits$ + +Likewise, the predicate ``related_ops`` defines when an operation +``bop`` on bounded integers is equivalent to an operation ``iop`` on +mathematical integers. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: related_ops$ + :end-before: //SNIPPET_END: related_ops$ + +Typeclass Inheritance +..................... + +Our ``bounded_unsigned_int a`` class just showed that ``a`` is in a +bijection with natural numbers below some bound. Now, we can define a +separate class, extending ``bounded_unsigned_int`` with the operations +we want, like addition, subtraction, etc. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: bui_ops$ + :end-before: //SNIPPET_END: bui_ops$ + +The class above makes use of *typeclass inheritance*. The ``base`` +field stores an instance of the base class ``bounded_unsigned_int``, +while the remaining fields extend it with: + + * ``add``: a bounded addition operation + * ``sub``: a bounded subtraction operation + * ``lt`` : a comparison function + * ``properties``, which show that + - ``add`` is related to integer addition ``+`` + - ``sub`` is related to integer subtraction ``-`` + - ``lt`` is related to ``<`` + - and that ``sub bound x`` is always safe + +Typeclass inheritance in the form of additional fields like ``base`` +is completely flexible, e.g., multiple inheritance is permissible +(though, as we'll see below, should be used with care, to prevent +surprises). + +Treating an instance of a class as an instance of one its base classes +is easily coded as instance-generating function. The code below says +that an instance from ``bounded_unsigned_int a`` can be derived from +an instance of ``d : bounded_unsigned_int_ops a`` just by projecting +its ``base`` field. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: ops_base$ + :end-before: //SNIPPET_END: ops_base$ + + +Infix Operators +............... + +F* does not allows the fields of a record to be named using infix +operator symbols. This will likely change in the future. For now, +to use custom operations with infix notation for typeclass methods, +one has to define them by hand: + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: ops$ + :end-before: //SNIPPET_END: ops$ + +Derived Instances +................. + +We've already seen how typeclass inheritance allows a class to induce +an instance of its base class(es). However, not all derived instances +are due to explicit inheritance---some instances can be *computed* +from others. + +For example, here's a class ``eq`` for types that support decidable +equality. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: eq$ + :end-before: //SNIPPET_END: eq$ + +We'll write ``x =?= y`` for an equality comparison method from this +class, to not confuse it with F*'s built-in decidable equality ``(=)`` +on ``eqtype``. + +Now, from an instance of ``bounded_unsigned_int_ops a`` we can +compute an instance of ``eq a``, since we have ``<^``, a strict +comparison operator that we know is equivalent to ``<`` on natural +numbers. F*, from all the properties we have on +``bounded_unsigned_int_ops`` and its base class +``bounded_unsigned_int``, can automatically prove that ``not (x <^ y) +&& not (y <^ x)`` is valid if and only if ``x == y``. This instance of +``eq`` now also lets us easily implement a non-strict comparison +operation on bounded unsigned ints. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: bui_eq$ + :end-before: //SNIPPET_END: bui_eq$ + +Ground Instances +................ + +We can easily provide ground instances of ``bounded_unsigned_int_ops`` +for all the F* bounded unsigned int types---we show instances for +``FStar.UInt32.t`` and ``FStar.UInt64.t``, where the proof of all the +properties needed to construct the instances is automated. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: ground_instances$ + :end-before: //SNIPPET_END: ground_instances$ + +And one can check that typeclass resolution works well on those ground +instances. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: ground_tests$ + :end-before: //SNIPPET_END: ground_tests$ + +Finally, as promised at the start, we can write functions that are +generic over all bounded unsigned integers, something we couldn't do +with interfaces alone. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: sum$ + :end-before: //SNIPPET_END: sum$ + +F* can prove that the bounds check in ``sum`` is sufficient to prove +that the addition does not overflow, and further, that the two tests +return ``Some _`` without failing due to overflow. + +However, the proving that ``Some? (sum [0x01ul; 0x02ul; 0x03ul] +0x00ul)`` using the SMT solver alone can be expensive, since it +requires repeated unfolding of the recursive function ``sum``--such +proofs are often more easily done using F*'s normalizer, as shown +below---we saw the ``assert_norm`` construct in a :ref:`previous +section `. + +.. literalinclude:: ../code/TypeclassesAlt3.fst + :language: fstar + :start-after: //SNIPPET_START: testsum32'$ + :end-before: //SNIPPET_END: testsum32'$ + +.. note :: + + That said, by using dependently typed generic programming (which we + saw a bit of :ref:`earlier `), it is + possible to write programs that abstract over all machine integer + types without using typeclasses. The F* library ``FStar.Integers`` + shows how that works. Though, the typeclass approach shown here is + more broadly applicable and extensible. + +Dealing with Diamonds +--------------------- + +One may be tempted to factor our ``bounded_unsigned_int_ops`` +typeclass further, separating out each operation into a separate +class. After all, it may be the case that some instances of +``bounded_unsigned_int`` types support only addition while others +support only subtraction. However, when designing typeclass +hierarchies one needs to be careful to not introduce coherence +problems that result from various forms of multiple inheritance. + +Here's a typeclass that captures only the subtraction operation, +inheriting from a base class. + +.. literalinclude:: ../code/TypeclassesAlt2.fst + :language: fstar + :start-after: //SNIPPET_START: subtractable$ + :end-before: //SNIPPET_END: subtractable$ + +And here's another typeclass that, say, provides only the comparison +operation, also inheriting from the base class. + +.. literalinclude:: ../code/TypeclassesAlt2.fst + :language: fstar + :start-after: //SNIPPET_START: comparable$ + :end-before: //SNIPPET_END: comparable$ + +However, now when writing programs that expect both subtractable and +comparable integers, we end up with a coherence problem. + +The ``sub`` operation fails to verify, with F* complaining that it +cannot prove ``fits op_Subtraction bound acc``, i.e., this ``sub`` may +underflow. + +.. literalinclude:: ../code/TypeclassesAlt2.fst + :language: fstar + :start-after: //SNIPPET_START: try_sub_fail$ + :end-before: //SNIPPET_END: try_sub_fail$ + +At first, one may be surprised, since the ``s : +subtractable_bounded_unsigned_int a`` instance tells us that +subtracting from the ``bound`` is always safe. However, the term +``bound`` is an overloaded (nullary) operator and there are two ways +to resolve it: ``s.base.bound`` or ``c.base.bound`` and these two +choices are not equivalent. In particular, from ``s : +subtractable_bounded_unsigned_int a``, we only know that +``s.base.bound `sub` acc`` is safe, not that ``c.base.bound `sub` +acc`` is safe. + +Slicing type typeclass hierarchy too finely can lead to such coherence +problems that can be hard to diagnose. It's better to avoid them by +construction, if at all possible. Alternatively, if such problems do +arise, one can sometimes add additional preconditions to ensure that +the multiple choices are actually equivalent. There are many ways to +do this, ranging from indexing typeclasses by their base classes, to +adding equality hypotheses---the equality hypothesis below is +sufficient. + +.. literalinclude:: ../code/TypeclassesAlt2.fst + :language: fstar + :start-after: //SNIPPET_START: try_sub$ + :end-before: //SNIPPET_END: try_sub$ + +Overloading Monadic Syntax +-------------------------- + +We now look at some examples of typeclasses for *type functions*, in +particular, typeclasses for functors and monads. + +.. note :: + + If you're not familiar with monads, referring back to :ref:`A First + Model of Computational Effects ` may help. + +In :ref:`a previous chapter `, we introduced syntactic +sugar for monadic computations. In particular, F*'s syntax supports +the following: + +* Instead of writing ``bind f (fun x -> e)`` you can define a custom + ``let!``-operator and write ``let! x = f in e``. + +* And, instead of writing ``bind f (fun _ -> e)`` you can write + ``f ;! e``. + +Now, if we can overload the symbol ``bind`` to work with any monad, +then the syntactic sugar described above would work for all of +them. This is accomplished as follows. + +We define a typeclass ``monad``, with two methods ``return`` and ``( +let! )``. + +.. literalinclude:: ../code/MonadFunctorInference.fst + :language: fstar + :start-after: //SNIPPET_START: monad$ + :end-before: //SNIPPET_END: monad$ + +Doing so introduces ``return`` and ``( let! )`` into scope at the +following types: + +.. code-block:: fstar + + let return #m {| d : monad m |} #a (x:a) : m a = d.return x + let ( let! ) #m {| d : monad m |} #a #b (f:m a) (g: a -> m b) : m b = d.bind f g + +That is, we now have ``( let! )`` in scope at a type general enough to +use with any monad instance. + +.. note:: + + There is nothing specific about ``let!``; F* allows you to add a + suffix of operator characters to the ``let``-token. See this file + for more examples of `monadic let operators + `_ + +The type ``st s`` is a state monad parameterized by the state ``s``, +and ``st s`` is an instance of a ``monad``. + +.. literalinclude:: ../code/MonadFunctorInference.fst + :language: fstar + :start-after: //SNIPPET_START: st$ + :end-before: //SNIPPET_END: st$ + +With some basic actions ``get`` and ``put`` to read and write the +state, we can implement ``st`` computations with a syntax similar to +normal, direct-style code. + +.. literalinclude:: ../code/MonadFunctorInference.fst + :language: fstar + :start-after: //SNIPPET_START: get_inc$ + :end-before: //SNIPPET_END: get_inc$ + +Of course, we can also do proofs about our ``st`` computations: here's +a simple proof that ``get_put`` is ``noop``. + +.. literalinclude:: ../code/MonadFunctorInference.fst + :language: fstar + :start-after: //SNIPPET_START: get_put$ + :end-before: //SNIPPET_END: get_put$ + +Now, the nice thing is that since ``( let! )`` is monad polymorphic, +we can define other monad instances and still use the syntactic sugar +to build computations in those monads. Here's an example with the +``option`` monad, for computations that may fail. + +.. literalinclude:: ../code/MonadFunctorInference.fst + :language: fstar + :start-after: //SNIPPET_START: opt_monad$ + :end-before: //SNIPPET_END: opt_monad$ + +Exercise +........ + +Define a typeclass for functors, type functions ``m: Type -> Type`` +which support the operations ``fmap : (a -> b) -> m a -> m b``. + +Build instances of ``functor`` for a few basic types, e.g., ``list``. + +Derive an instance for functors from a monad, i.e., prove + +``instance monad_functor #m {| monad m |} : functor m = admit()`` + +This `file <../code/exercises/Part3.MonadsAndFunctors.fst>`_ provides +the definitions you need. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/MonadFunctorInference.fst + :language: fstar + :start-after: //SNIPPET_START: functor$ + :end-before: //SNIPPET_END: functor$ + +-------------------------------------------------------------------------------- + +Beyond Monads with Let Operators +-------------------------------- + +Many monad-like structures have been proposed to structure effectful +computations. Each of these structures can be captured as a typeclass +and used with F*'s syntactic sugar for let operators. + +As an example, we look at *graded monads*, a construction studied by +Shin-Ya Katsumata and others, `in several papers +`_. This +example illustrates the flexibility of typeclasses, including +typeclasses for types that themselves are indexed by other +typeclasses. + +The main idea of a graded monad is to index a monad with a monoid, +where the monoid index characterizes some property of interest of the +monadic computation. + +A monoid is a typeclass for an algebraic structure with a single +associative binary operation and a unit element for that +operation. A simple instance of a monoid is the natural numbers with +addition and the unit being ``0``. + +.. literalinclude:: ../code/GradedMonad.fst + :language: fstar + :start-after: //SNIPPET_START: monoid$ + :end-before: //SNIPPET_END: monoid$ + +A graded monad is a type constructor ``m`` indexed by a monoid as +described by the class below. In other words, ``m`` is equipped with +two operations: + + * a ``return``, similar to the ``return`` of a monad, but whose + index is the unit element of the monoid + + * a ``( let+ )``, similar to the ``( let! )`` of a monad, but whose + action on the indexes corresponds to the binary operator of the + indexing monoid. + +.. literalinclude:: ../code/GradedMonad.fst + :language: fstar + :start-after: //SNIPPET_START: graded_monad$ + :end-before: //SNIPPET_END: graded_monad$ + +With this class, we have overloaded ``( let+ )`` to work with all +graded monads. For instance, here's a graded state monad, ``count_st`` +whose index counts the number of ``put`` operations. + +.. literalinclude:: ../code/GradedMonad.fst + :language: fstar + :start-after: //SNIPPET_START: counting$ + :end-before: //SNIPPET_END: counting$ + +We can build computations in our graded ``count_st`` monad relatively +easily. + +.. literalinclude:: ../code/GradedMonad.fst + :language: fstar + :start-after: //SNIPPET_START: test$ + :end-before: //SNIPPET_END: test$ + +F* infers the typeclass instantiations and the type of ``test`` to be +``count_st s (op #monoid_nat_plus 0 1) unit``. + +In ``test2``, F* infers the type ``count_st s (op #monoid_nat_plus 0 +(op #monoid_nat_plus 1 1)) unit``, and then automatically proves that +this type is equivalent to the user annotation ``count_st s 2 unit``, +using the definition of ``monoid_nat_plus``. + +Summary +------- + +Typeclasses are a flexible way to structure programs in an abstract +and generic style. Not only can this make program construction more +modular, in can also make proofs and reasoning more abstract, +particularly when typeclasses contain not just methods but also +properties characterizing how those methods ought to behave. Reasoning +abstractly can make proofs simpler: for example, if the monoid-ness of +natural number addition is the only property needed for a proof, it +may be simpler to do a proof generically for all monoids, rather than +reasoning specifically about integer arithmetic. + +That latter part of this chapter presented typeclasses for +computational structures like monads and functors. Perhaps conspicuous +in these examples were the lack of algebraic laws that characterize +these structures. Indeed, we focused primarily on programming with +monads and graded monads, rather than reasoning about them. Enhancing +these typeclasses with algebraic laws is a useful, if challenging +exercise. This also leads naturally to F*'s effect system in the next +section of this book, which is specifically concerned with doing +proofs about programs built using monad-like structures. diff --git a/book/part4/part4.rst b/book/part4/part4.rst new file mode 100644 index 00000000000..a52a764eb17 --- /dev/null +++ b/book/part4/part4.rst @@ -0,0 +1,73 @@ +.. _Part4: + +Computational Effects +===================== + +All the programs we've considered so far have been *pure*, which is to +say that only thing that can be observed about a program by its +context is the value that the program returns---the inner workings of +the program (e.g., whether it multiplies two numbers by repeated +addition or by using a primitive operation for multiplication) cannot +influence the behavior of its context. That is, pure terms can be +reasoned about like pure mathematical functions. [#]_ + +However, many practical programs exhibit behaviors that are beyond +just their output. For example, they may mutate some global state, or +they may read and write files, or receive or send messages over the +network---such behaviors are often called *side effects*, +*computational effects*, or just *effects*. + +In this section, we look at F*'s effect system which allows users to + + * Model the semantics of various kinds of effects, e.g., mutable + state, input/output, concurrency, etc. + + * Develop reasoning principles that enable building proofs of + various properties of effectful programs + + * Simplify effectful program construction by encapsulating the + semantic model and reasoning principles within an abstraction that + allows users to write programs in F*'s native syntax while behind + the scenes, for reasoning and execution purposes, programs are + elaborated into the semantic model + +Aside from user-defined effects, F*'s also supports the following +*primitive* effects: + + * **Ghost**: An effect which describes which parts of a program have + no observable behavior at all, and do not even influence the + result returned by the program. This allows optimizing a program + by erasing the parts of a program that are computationally + irrelevant. + + * **Divergence**: An effect that encapsulates computations which may + run forever. Potentially divergent computations cannot be used as + proofs (see :ref:`termination`) and the effect + system ensures that this is so. + + * **Partiality**: Partial functions are only defined over a subset + of their domain. F* provides primitive support for partial + functions as an effect. Although currently primitive, in the + future, we hope to remove the special status of partial functions + and make partial functions a user-defined notion too. + + +.. [#] Although pure F* programs are mathematical functions + in the ideal sense, when executing these programs on a + computer, they do exhibit various side effects, including + consuming resources like time, power, and memory. Although + these side effects are clearly observable to an external + observer of a running F* program, the resourse-usage side + effects of one component of a pure F* program are not visible + to another component of the same program. + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + part4_background + part4_computation_types_and_tot + part4_ghost + part4_div + part4_pure + diff --git a/book/part4/part4_background.rst b/book/part4/part4_background.rst new file mode 100644 index 00000000000..c980bc29ab7 --- /dev/null +++ b/book/part4/part4_background.rst @@ -0,0 +1,93 @@ +.. _Part4_Background: + +Computation Types to Track Dependences +====================================== + +A main goal for F*'s effect system is to *track dependences* among the +various parts of a program. For example, the effect system needs to +ensures that the total part of a program that is proven to always +terminate never calls a function in the divergent fragment (since that +function call may loop forever). Or, that the runtime behavior of a +compiled program does not depend on ghost computations that get erased +by the compiler. + +In a paper from 1999 called `A Core Calculus of Dependency +`_, Abadi et +al. present DCC, a language with a very generic way to track +dependences. DCC's type system includes an indexed, monadic type +:math:`T_l`, where the index :math:`l` ranges over the elements of a +lattice, i.e., the indexes are arranged in a partial order. DCC's type +system ensures that a computation with type :math:`T_l` can depend on +another computation with type :math:`T_m` only if :math:`m \leq l` in +the lattice's partial order. F*'s effect system is inspired by DCC, +and builds on a 2011 paper by Swamy et al. called `Lightweight Monadic +Programming in ML `_ +which develops a DCC-like system for an ML-like programming language. + +At its core, F*'s effect system includes the following three elements: + +**Computation Types**: F*'s type system includes a notion of +*computation type*, a type of the form ``M t`` where ``M`` is an +*effect label* and ``t`` is the return type of the computation. A term +``e`` can be given the computation type ``M t`` when executing ``e`` +exhibits *at-most* the effect ``M`` and (possibly) returns a value of +type ``t``. We will refine this intuition as we go along. In contrast +with computation types, the types that we have seen so far (``unit``, +``bool``, ``int``, ``list int``, other inductive types, refinement +types, and arrows) are called *value types*. + +**Partially Ordered Effect Labels**: The effect label of a computation +type is drawn from an open-ended, user-extensible set of labels, where +the labels are organized in a user-chosen partial order. For example, +under certain conditions, one can define the label ``M`` to be a +sub-effect of ``N``, i.e., ``M < N``. For any pair of labels ``M`` +and ``N``, a partial function ``lub M N`` (for least upper bound) +computes the least label greater than both ``M`` and ``N``, if any. + +**Typing Rules to Track Dependences**: The key part of the effect +system is a rule for composing computations sequentially using ``let x += e1 in e2``. Suppose ``e1 : M t1``, and suppose ``e2 : N t2`` +assuming ``x:t1``, then the composition ``let x = e1 in e2`` has type +``L t2``, where ``L = lub M N``---if ``lub M N`` is not defined, then +the ``let``-binding is rejected. Further, a computation with type ``M +t`` can be implicitly given the type ``N t``, when ``M < N``, i.e., +moving up the effect hierarchy is always permitted. The resulting +typing discipline enforces the same dependence-tracking property as +DCC: a computation ``M t`` can only depend on ``N t`` when ``lub M N = +M``. + +In full generality, F*'s computation types are more complex than just +an effect label ``M`` and a result type (i.e., more than just ``M +t``), and relying on F*'s dependent types, computation types do more +than just track dependences, e.g., a computation type in F* can also +provide full, functional correctness specifications. The papers +referenced below provide some context and we discuss various elements +of these papers throughout this part of the book. + + + + `Verifying Higher-order Programs with the Dijkstra Monad + `_, introduces the + idea of a Dijkstra monad, a construction to structure the + inference of weakest preconditions of effectful computations. + + + This 2016 paper, + `Dependent Types and Multi-Monadic Effects in F* `_, + has become the canonical reference for F\*. It shows how to combine + multiple Dijkstra monads with a DCC-like system. + + + `Dijkstra Monads for Free + `_ presents an + algorithm to construct Dijkstra monads automatically for a class + of simple monads. + + + `Dijkstra Monads for All + `_ generalizes the + construction to apply to relate any computational monad to a + specificational counterpart, so long as the two are related by a + monad morphism. + + + `Programming and Proving with Indexed Effects + `_ describes + F*'s user-defined effect system in its most general form, allowing + it to be applied to any indexed effects, including Dijkstra + monads, but several other constructions as well. diff --git a/book/part4/part4_computation_types_and_tot.rst b/book/part4/part4_computation_types_and_tot.rst new file mode 100644 index 00000000000..4a6842c2ae2 --- /dev/null +++ b/book/part4/part4_computation_types_and_tot.rst @@ -0,0 +1,93 @@ +.. _Part4_Computation_Types_And_Tot: + +The Effect of Total Computations +================================ + +At the very bottom of the effect label hierarchy is ``Tot``, used to +describe pure, total functions. Since they are at the bottom of the +hierarchy, ``Tot`` computations can only depend on other ``Tot`` +computations, ensuring that F*'s logical core remains total. + +Every term in F* is typechecked to have a computation type. This +includes the total terms we have been working with. Such terms are +classified in the default effect called ``Tot``, the effect of total +computations that do not have any observable effect, aside from the +value they return. Any meaning or intuition that we have ascribed to +typing ``e:t`` extends to ``e:Tot t``. For example, if ``e:Tot t``, +then at runtime, ``e`` terminates and produces a value of type +``t``. In addition, since its effect label is ``Tot``, it has no +side-effect. + +In fact, as we have already :ref:`seen `, +notationally ``x:t0 -> t1`` is a shorthand for ``x:t0 -> Tot t1`` +(where ``t1`` could itself be an arrow type). More generally, arrow +types in F* take the form ``x:t -> C``, representing a function with +argument type ``t0`` and body computation type ``C`` (which may depend +on ``x``). + +Similarly, the return type annotation that we have seen in the ``let`` +definitions is also a shorthand, e.g., the :ref:`id function +` + +.. code-block:: fstar + + let id (a:Type) (x:a) : a = x + +is a shorthand for + +.. code-block:: fstar + + let id (a:Type) (x:a) : Tot a = x //the return type annotation is a computation type + +and the type of ``id`` is ``a:Type -> a -> Tot a``. More generally, +the return type annotations on ``let`` definitions are computation +types ``C``. + +The :ref:`explicit annotation syntax ` ``e +<: t`` behaves a little differently. F* allows writing ``e <: C``, and +checks that ``e`` indeed has computation type ``C``. But when the +effect label is omitted, ``e <: t``, it is interpreted as ``e <: _ +t``, where the omitted effect label is inferred by F* and does not +default to ``Tot``. + + +.. _Part4_evaluation_order: + +Evaluation order +^^^^^^^^^^^^^^^^ + +For pure functions, the evaluation order is irrelevant. [#]_ F* +provides abstract machines to interpret pure terms using either a lazy +evaluation strategy or a call-by-value strategy (see a +:ref:`forthcoming chapter on F*'s normalizers +`). Further, compiling pure programs to OCaml, F* +inherits the OCaml's call-by-value semantics for pure terms. + +When evaluating function calls with effectful arguments, the arguments +are reduced to values first, exhibiting their effects, if any, prior +to the function call. That is, where ``e1`` and ``e2`` may be +effectful, the application ``e1 e2`` is analogous to ``bind f = e1 in +bind x = e2 in f x``: in fact, internally this is what F* elaborates +``e1 e2`` to, when either of them may have non-trivial effects. As +such, for effectful terms, F* enforces a left-to-right, `call-by-value +`_ semantics for +effectful terms. + +Since only the value returned by a computation is passed as an +argument to a function, function argument types in F* are always value +types. That is, they always have the form ``t -> C``. To pass a +computation as an argument to another function, you must encapsulate +the computation in a function, e.g., in place of ``C -> C``, one can +write ``(unit -> C) -> C'``. Since functions are first-class values in +F*, including functions whose body may have non-trivial effects, one +can always do this. + +.. [#] Since F* is an extensional type theory, pure F* terms are only + *weakly* normalizing. That is, some evaluation strategies + (e.g., repeatedly reducing a recursive function deep in an + infeasible code path) need not terminate. However, for every + closed, pure term, there is a reduction strategy that will + reduce it fully. As such, the evaluation order for pure + functions is irrelevant, except that some choices of evaluation + order may lead to non-termination. + diff --git a/book/part4/part4_div.rst b/book/part4/part4_div.rst new file mode 100644 index 00000000000..f7e3c038d37 --- /dev/null +++ b/book/part4/part4_div.rst @@ -0,0 +1,547 @@ +.. _Part4_Div: + +Divergence, or Non-Termination +============================== + +Most dependently typed languages are not `Turing complete +`_. This is +because, as explained :ref:`earlier `, it is +crucial to the soundness of a type theory to have all functions +terminate. This means that you cannot program, say, an interpreter for +a general-purpose programming language in a language like Coq, since +such an interpreter would not be able to handle programs that +intentionally loop forever. [#]_ + +F*'s logical core of total (and ghost) functions can only express +terminating computations. However, F*'s also allows expressing +non-terminating or *divergent* computations, relying on the effect +system to isolate divergent computations from the logical core. In +particular, the computation type ``Dv t`` describes a computation that +may loop forever, but if it completes, it returns a value of type +``t``. + +Relying on the effect system as a dependency tracking mechanism, F* +ensures that ``Tot`` computations cannot rely on ``Dv`` computations +by placing ``Dv`` above ``Tot`` in the effect hierarchy, while, +conversely, a total computation ``Tot t`` can be silently promoted to +``Dv t``, the type of computations that may not terminate, i.e., ``Tot +< Dv`` in the effect partial order. + +Recursive functions that return computations in the ``Dv`` effect are +not checked for termination. As such, using the ``Dv`` effect, one +can write programs such as the one below, which computes `Collatz +sequences +`_---whether or not +this program terminates for all inputs is an open problem. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: collatz$ + :end-before: //SNIPPET_END: collatz$ + +In this chapter, we'll look in detail at the ``Dv`` effect and how it +interacts with other features of the language, including the other +effects, recursive type definitions, and the styles of programming and +proving it enables. + +.. [#] In place of general recursion and potential non-termination, + other dependently typed languages like Coq and Agda offer + features like corecursion and coinduction. Coinduction can be + used to express a class of *productive* non-terminating + programs. For instance, using coinduction, one could program a + web server that loops forever to handle an infinite stream of + requests, while producing a response for each request in a + finite amount of time. Even the ``collatz`` function can be + given a corecursive definition that computes a potentially + infinite stream of numbers. However, not all non-terminating + computations can be implemented with + coinduction/corecursion. F* does not yet support coinduction. + + +The ``Dv`` effect +^^^^^^^^^^^^^^^^^^^ + +The effect ``Dv`` (for divergence) is a primitive effect in F*. +Computations in ``Dv`` may not terminate, even with infinite +resources. In other words, computations in the ``Dv`` effect have the +observational behavior of non-termination. For example, the following +``loop`` function has type ``unit -> Dv unit`` and it always diverges +when called: + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: loop$ + :end-before: //SNIPPET_END: loop$ + +If we remove the ``Dv`` effect label annotation, then F* treats the +function as total and will try to prove that the recursive call +terminates, according to its usual termination checking rules, i.e., +F* will attempt to prove ``() << ()`` which fails, as expected. + +Since the ``Dv`` effect admits divergence, F* essentially turns-off +the termination checker when typechecking ``Dv`` computations. So the +recursive ``loop ()`` call does not require a decreasing termination +metric. + +Partial correctness semantics of ``Dv`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``Tot`` effect in F* has a *total correctness* semantics. That is, +if a term has type ``e:Tot t``, then ``e`` terminates terminates and +produces a value of type ``t``. + +Terms with type ``Dv t`` have a *partial correctness* semantics. That +is, a term ``e:Dv t``, ``e`` may either run forever, but if it +terminates then the resulting value has type ``t``. + +Another perspective is that aside from disabling the termination +checking features of F*, all other type-checking constraints are +enforced on ``Dv`` term. This means that one can still give +interesting sound, specifications to ``Dv`` programs, e.g., the type +below proves that if the Collatz function terminates, then the last +element of the sequence is ``1``. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: collatz_ends_in_one$ + :end-before: //SNIPPET_END: collatz_ends_in_one$ + +If, for example, in the base case we were to return the empty list +``[]`` rather than ``[n]``, then F* would refuse to accept the +program, since the program could terminate while returning a value +that is not an element of the annotated return type. + +Isolating ``Dv`` from the logical core +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Since ``Dv`` terms need not terminate, a program that always loops +forever can be given any return type. For instance, the program below +has return type ``False``: + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: loop_false$ + :end-before: //SNIPPET_END: loop_false$ + +Importantly, a term of type ``Dv False`` should not be confused as a +*proof* of ``False``, since that would lead immediately to unsoundness +of F*'s logical core. In particular, it should be impossible to turn a +``e:Dv t`` into a term of type ``Tot t``. This is achieved by F*'s +effect system, which treats ``Tot`` as a sub-effect of ``Dv``, i.e., +``Tot < Dv``, in the effect order. As explained in :ref:`earlier +`, this ensures that no ``Tot`` term can depend on a +``Dv`` term, maintaining soundness of the total correctness +interpretation of ``Tot``. + +As an example, the following attempt to "cast" ``dv_false`` to ``Tot`` +fails, as does trying to use ``dv_false`` to produce incorrect proofs +of other types. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: loop_false_failures$ + :end-before: //SNIPPET_END: loop_false_failures$ + + +While F* does not allow ``Tot`` computations to depend on ``Dv`` +computations, going the other way is perfectly fine. Intuitively, +always terminating computations are potentially non-terminating. We +can think of it like a *weakening* of the specification: + +.. code-block:: fstar + + let add_one (x:int) : int = x + 1 + let add_one_div (x:int) : Dv int = add_one x + +The effect system of F* automatically *lifts* ``Tot`` computations +into ``Dv``, meaning that ``Tot`` functions can be seamlessly used in +``Dv`` functions. + +The weakening of ``Tot`` terms to other effects is so pervasive in F* +that one hardly even thinks about it, e.g., in the ``collatz`` +program, sub-terms like ``n / 2`` are in ``Tot`` but are easily used +within a computation in the ``Dv`` effect. + +No extrinsic proofs for ``Dv`` computations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +One important consequence of any effectful code, including ``Dv``, +being outside the logical core of F* is that it is not possible to do +:ref:`extrinsic proofs ` about effectful +code. One cannot even state properties of ``Dv`` computations in +specifications, since even specifications must be total. For example, +even stating the following lemma is illegal: + +.. code-block:: fstar + + [@@expect_failure] + val collatz_property (n:pos) + : Lemma (Cons? (collatz n) /\ last (collatz n) = 1) + +This is nonsensical in F* since writing ``Cons? (collatz n)`` supposes +that ``collatz n`` is *defined*, whereas it might actually just loop +forever. + +The only way to state properties about divergent programs is to encode +the property intrinsically in the computation type, as we saw above. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: val collatz_ends_in_one$ + :end-before: //SNIPPET_END: val collatz_ends_in_one$ + +Exercise +++++++++ + +Define a predicate ``collatz_spec (n:pos) (l:list pos) : bool`` that +decides if ``l`` is a valid Collatz sequence starting at ``n``. + +Implement ``val collatz' (n:pos) : Dv (l:list pos { collatz_spec n l })``. + +What does this type mean? Are there other ways to implement +``collatz'`` with the same type? + + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: collatz_spec$ + :end-before: //SNIPPET_END: collatz_spec$ + +-------------------------------------------------------------------------------- + +General Recursive Types and Impredicativity with ``Dv`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Aside from disabling the decreases metric on recursive functions in +``Dv``, F* also disables two other forms of termination checking on +``Dv`` computations. + +Recall from a :ref:`previous chapter ` that +inductive type definitions are subject to the *strict positivity* +condition, since non-positive definitions allow the definition of +recursive types and non-terminating computations. However, since +computations in the ``Dv`` effect are already allowed to loop forever, +the strict positivity condition can be relaxed when ``Dv`` types are +involved. For example, one can define this: + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: nonpos$ + :end-before: //SNIPPET_END: nonpos$ + +The type ``nonpos`` is not strictly positive, since it appears to the +left of an arrow in a field of one of its constructors. Indeed, usingn +``nonpos`` it is possible to define (without using ``let rec``) an +infinitely looping program ``loop_nonpos()``---however, the type ``Dv +False`` tells us that this program may loop forever, and the infinite +loop is safely isolated from F*'s logical core. + +The other place in F*'s type system where termination checking comes +into play is in the :ref:`universe levels `. As we +learned previously, the logical core of F* is organized into an +infinite hierarchy with copies of the F* type system arranged in a +tower of universes. This stratification is necessary to prevent +inconsistencies within the logical core. However, terms in the ``Dv`` +effect are outside the logical core and, as such, restrictions on the +universe levels no longer apply. As the snippet below shows a total +function returning a type in universe ``u#a`` resides in universe +``u#(a + 1)``. However, a ``Dv`` function returning a type in ``u#a`` +is just in universe ``0``, since the only way to obtain the type +``dv_type`` returns is by incurring a ``Dv`` effect and moving outside +F*'s logical core. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: universe_dv$ + :end-before: //SNIPPET_END: universe_dv$ + +Top-level Effects +^^^^^^^^^^^^^^^^^ + +A top-level F* term is not meant to be effectful. If one defines the +following term, F* accepts the term but raises a warning saying +"Top-level let bindings must be total---this term may have effects". + +.. code-block:: fstar + + let inconsistent : False = loop_nonpos() + +Top-level effects can be problematic for a few reasons: + + 1. The order of evaluation of the effects in top-level terms is + undefined for programs with multiple modules---it depends on the + order in which modules are loaded at runtime. + + 2. Top-level effects, particularly when divergence is involved, can + render F*'s typechecking context inconsistent. For example, once + ``inconsistent`` is defined, then any other assertion can be + proven. + + .. code-block :: fstar + + let _ = let _ = FStar.Squash.return_squash inconsistent in + assert false + +Nevertheless, when used carefully, top-level effects can be useful, +e.g., to initialize the state of a module, or to start the main +function of a program. So, pay attention to the warning F* raises when +you have a top-level effect and make sure you really know what you're +doing. + +Example: Untyped Lambda Calculus +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In this section, we put together the various things we've learned +about ``Dv`` computations to define several variants of an untyped +lambda calculus. + +You can refer back to our prior development of the :ref:`simply typed +lambda calculus ` if you need some basic background on the +lambda calculus. + +Interpreting Deeply Embedded Lambda Terms ++++++++++++++++++++++++++++++++++++++++++ + +We start by defining the syntax of untyped lambda terms, below. The +variables use the de Bruijn convention, where a index of a variable +counts the number of lambda-binders to traverse to reach its binding +occurrence. The ``Lam`` case just has the body of the lambda term, +with no type annotation on the binder, and no explicit name for the +variable. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: deep_embedding_syntax$ + :end-before: //SNIPPET_END: deep_embedding_syntax$ + +As usual, we can define what it means to substitute a variable ``x`` +with a (closed) term ``v`` in ``t``---this is just a regular ``Tot`` +function. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: deep_embedding_subst$ + :end-before: //SNIPPET_END: deep_embedding_subst$ + +Finally, we can define an interpreter for ``term``, which can +(intentionally) loop infinitely, as is clear from the ``Dv`` type +annotation. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: deep_embedding_interpreter$ + :end-before: //SNIPPET_END: deep_embedding_interpreter$ + +Exercise +........ + +This exercise is designed to show how you can prove non-trivial +properties of ``Dv`` computations by giving them interesting dependent +types. + +The substitution function defined here is only sound when the term +being substituted is closed, otherwise, any free variables it has can +be captured when substituted beneath a lambda. + +A term is closed if it satisfies this definition: + +.. literalinclude:: ../code/Part4.UTLCEx1.fst + :language: fstar + :start-after: //SNIPPET_START: closed$ + :end-before: //SNIPPET_END: closed$ + +Restrict the type of ``subst`` so that its argument is ``v : term { +closed v }``---you will have to also revise the type of its other +argument for the proof to work. + +Next, give the following type to the interpreter itself, proving that +interpreting closed terms produces closed terms, or loops forever. + +.. literalinclude:: ../code/Part4.UTLCEx1.fst + :language: fstar + :start-after: //SNIPPET_START: interpret$ + :end-before: //SNIPPET_END: interpret$ + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part4.UTLCEx1.fst + :language: fstar + +-------------------------------------------------------------------------------- + + +Denoting Lambda Terms into an F* Recursive Type ++++++++++++++++++++++++++++++++++++++++++++++++ + +We now look at a variation on the interpreter above to illustrate how +(non-positive) recursive types using ``Dv`` can also be used to give a +semantics to untyped lambda terms. + +Consider the type ``dyn`` shown below---it has a non-positive +constructor ``DFun``. We can use this type to interpret untyped lambda +terms into dynamically typed, potentially divergent, F* terms, +showing, in a way, that untyped lambda calculus is no more expressive +than F* with the ``Dv`` effect. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: dyn$ + :end-before: //SNIPPET_END: dyn$ + +The program ``denote`` shown below gives a semantics to ``term`` using +``dyn``. It is parameterized by a ``ctx : ctx_t``, which interprets +the free variables of the term into ``dyn``. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: denote$ + :end-before: //SNIPPET_END: denote$ + +We look at the cases in detail: + + * In the ``Var`` case, the intepretation just refers to the context. + + * Integers constants in ``term`` are directly interpreted to + integers in ``dyn``. + + * The case of ``Lam`` is the most interesting: An lambda abstraction + in ``term`` is interpreted as an F* function ``dyn -> Dv dyn``, + recursively calling the denotation function on the body when the + function is applied. Here's where we see the non-positivity of + ``DFun`` at play---it allows us to inject the function into the + ``dyn`` type. + + * Finally, in the application case, we interpret a syntactic + application in ``term`` as function application in F* (unless the + head is not a function, in which case we have a type error). + +Exercise +........ + +This exercise is similar in spirit to the previous one and designed to +show that you can prove some simple properties of ``denote`` by +enriching its type. + +Can you prove that a closed term can be interpreted in an empty +context? + +First, let's refine the type of contexts so that it only provides an +interpretation to only some variables: + +.. literalinclude:: ../code/Part4.UTLCEx2.fst + :language: fstar + :start-after: //SNIPPET_START: ctx_t$ + :end-before: //SNIPPET_END: ctx_t$ + +Next, let's define ``free t`` to compute the greatest index of a free +variable in a term. + +.. literalinclude:: ../code/Part4.UTLCEx2.fst + :language: fstar + :start-after: //SNIPPET_START: free$ + :end-before: //SNIPPET_END: free$ + +Can you give the same ``denote`` function shown earlier the following +type? + +.. code-block:: fstar + + val denote (t:term) (ctx:ctx_t (free t)) + : Dv dyn + +Next, define the empty context as shown below: + +.. literalinclude:: ../code/Part4.UTLCEx2.fst + :language: fstar + :start-after: //SNIPPET_START: empty_context$ + :end-before: //SNIPPET_END: empty_context$ + +Given a closed term ``t : term { closed t }``, where ``closed t = +(free t = -1)``, can you use ``denote`` to give an interpretation to +closed terms in the empty context? + + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part4.UTLCEx2.fst + :language: fstar + +-------------------------------------------------------------------------------- + +Shallowly Embedded Dynamically Typed Programming +++++++++++++++++++++++++++++++++++++++++++++++++ + +In the previous example, we saw how the syntax of untyped lambda terms +can be interpreted into the F* type ``dyn``. In this example, rather +than going via the indirection of the syntax of lambda terms, we show +how the type ``dyn`` can be used directly to embed within F* a small +Turing complete, dynamically typed programming language. + +We can start by lifting the F* operations on integers and functions to +(possibly failing) operations on ``dyn``. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: lift_int$ + :end-before: //SNIPPET_END: lift_int$ + +We also encode provide operations to compare dyn-typed integers and to +branch on them, treating ``0`` as ``false``. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: branch_eq$ + :end-before: //SNIPPET_END: branch_eq$ + +For functions, we can provide combinators to apply functions and, +importantly, a combinator ``fix`` that provides general recursion. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: app_fix$ + :end-before: //SNIPPET_END: app_fix$ + +An aside on the arity of recursive functions: You may wonder why +``fix`` is defined as shown, rather than ``fix_alt`` below, which +removes a needless additional abstraction. The reason is that with +``fix_alt``, to instruct F* to disable the termination checker on the +recursive definition, we need an additional ``Dv`` annotation: indeed, +evaluating ``fixalt f`` in a call-by-value semantics would result, +unconditionally, in an infinite loop, whereas ``fix f`` would +immediately return the lambda term ``fun n -> f (fix f) n``. In other +words, eta reduction (or removing redundant function applications) +does not preserve semantics in the presence of divergence. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: fix_alt$ + :end-before: //SNIPPET_END: fix_alt$ + +With that, we can program non-trivial dynamically typed, general +recursive programs within F* itself, as seen below. + +.. literalinclude:: ../code/Divergence.fst + :language: fstar + :start-after: //SNIPPET_START: collatz_dyn$ + :end-before: //SNIPPET_END: collatz_dyn$ + +All of which is to illustrate that with general recursion and +non-positive datatypes using ``Dv``, F* is a general-purpose +programming language like ML, Haskell, Lisp, or Scheme, or other +functional languages you may be familiar with. + + diff --git a/book/part4/part4_dm4f.rst.outline b/book/part4/part4_dm4f.rst.outline new file mode 100644 index 00000000000..a7aaa8e5366 --- /dev/null +++ b/book/part4/part4_dm4f.rst.outline @@ -0,0 +1,4 @@ +.. _Part4_DM4F: + +Dijkstra Monads for Free +======================== diff --git a/book/part4/part4_ghost.rst b/book/part4/part4_ghost.rst new file mode 100644 index 00000000000..4174c8d5ff2 --- /dev/null +++ b/book/part4/part4_ghost.rst @@ -0,0 +1,526 @@ +.. _Part4_Ghost: + +Erasure and the Ghost Effect +============================ + +When writing proof-oriented programs, inevitably, some parts of the +program serve only to state and prove properties about the code that +actually executes. Our first non-trivial effect separates the +computationally relevant parts of the program from the computationally +irrelevant (i.e., specificational or *ghost*) parts of a program. This +separation enables the F* compiler to guarantee that all the ghost +parts of a program are optimized away entirely. + +For a glimpse of what all of this is about, let's take a look again at +length-indexed vectors---we saw them first :ref:`here `. + +.. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: //SNIPPET_START: vec + :end-before: //SNIPPET_END: vec + +and a function to concatenate two vectors: + +.. literalinclude:: ../code/Vec.fst + :language: fstar + :start-after: //SNIPPET_START: append + :end-before: //SNIPPET_END: append + +Compare this with concatenating two lists: + +.. code-block:: fstar + + let rec list_append #a (l1 l2:list a) = + match l1 with + | [] -> [] + | hd::tl -> hd :: list_append tl l2 + +Superficially, because of the implicit arguments, it may look like +concatenating vectors with ``append`` is just as efficient as a +concatenating lists---the length indexes seem to impose no +overhead. But, let's look at the code that F* extracts to OCaml for +length-indexed vectors. + +First, in the definition of the ``vec`` type, since OCaml is not +dependently typed, the ``nat``-index of the F* ``vec`` is replaced by +a ``'dummy`` type argument---that's fine. But, notice that the +``Cons`` constructor contains three fields: a ``Prims.nat`` for the +length of the tail of the list, the head of the list, and then then +tail, i.e., the length of the tail of the list is stored at every +``Cons`` cell, so the ``vec`` type is actually less space efficient +than an ordinary ``list``. + +.. literalinclude:: ../code/Vec.ml + :language: ocaml + :start-after: (* SNIPPET_START: vec *) + :end-before: (* SNIPPET_END: vec *) + +Next, in the OCaml definition of ``append``, we see that it receives +additional arguments ``n`` and ``m`` for the lengths of the vectors, +and worse, in the last case, it incurs an addition to sum ``n' + m`` +when building the result vector. So, ``append`` is also less +time-efficient than ``List.append``. + +.. literalinclude:: ../code/Vec.ml + :language: ocaml + :start-after: (* SNIPPET_START: append *) + :end-before: (* SNIPPET_END: append *) + +This is particularly unfortunate, since the computational behavior of +``append`` doesn't actually depend on the length indexes of the input +vectors. What we need is a principled way to indicate to the F\* +compiler that some parts of a computation are actually only there for +specification or proof purposes and that they can be removed when +compiling the code, without changing the observable result computed by +the program. This is what *erasure* is about---removing the +computationally irrelevant parts of a term for compilation. + +Here's a revised version of vectors, making use of the ``erased`` type +from the ``FStar.Ghost`` library to indicate to F* which parts must be +erased by the compiler. + +.. literalinclude:: ../code/VecErased.fst + :language: fstar + +We'll look into this in much more detail in what follows, but notice +for now that: + + 1. The first argument of ``Cons`` now has type ``erased nat``. + + 2. The implicit arguments of ``append`` corresponding to the + indexes of the input vectors have type ``erased nat``. + +If we extract this code to OCaml, here's what we get: + + +.. literalinclude:: ../code/VecErased.ml + :language: ocaml + :start-after: (* SNIPPET_START: vec *) + :end-before: (* SNIPPET_END: vec *) + +.. literalinclude:: ../code/VecErased.ml + :language: ocaml + :start-after: (* SNIPPET_START: append *) + :end-before: (* SNIPPET_END: append *) + +Notice that the erased arguments have all been turned into the unit +value ``()``, and the needless addition in ``append`` is gone too. + +Of course, the code would be cleaner if F* were to have entirely +removed the argument instead of leaving behind a unit term, but we +leave it to the downstream compiler, e.g., OCaml itself, to remove +these needless units. Further, if we're compiling the ML code +extracted from F* to C, then KaRaMeL does remove these additional +units in the C code it produces. + + +Ghost: A Primitive Effect +------------------------- + +The second, primitive effect in F*'s effect system is the effect of +*ghost* computations, i.e., computation types whose effect label is +``GTot``. [#]_ The label ``GTot`` is strictly above ``Tot`` in the +effect hierarchy, i.e., ``Tot < GTot``. This means that a term with +computation type ``GTot t`` cannot influence the behavior of a term +whose type is ``Tot s``. Conversely, every ``Tot`` computation can be +implicitly promoted to a ``GTot`` computation. + +Ghost computations are just as well-behaved as pure, total +terms---they always terminate on all inputs and exhibit no observable +effects, except for the value they return. As such, F*'s logical core +really includes both ``Tot`` and ``GTot`` computations. The +distinction between ``Tot`` and ``GTot`` is only relevant when +considering how programs are compiled. Ghost computations are +guaranteed to be erased by the the compiler, while ``Tot`` +computations are retained. + +Since ``Tot`` terms are implicitly promoted to ``GTot``, it is easy to +designate that some piece of code should be erased just by annotating +it with a ``GTot`` effect label. For example, here is an ghost version +of the factorial function: + +.. literalinclude:: ../code/FactorialTailRec.fst + :language: fstar + :start-after: //SNIPPET_START: factorial$ + :end-before: //SNIPPET_END: factorial$ + +Its definition is identical to the corresponding total function that +we saw earlier, except that we have annotated the return computation +type of the function as ``GTot nat``. This indicates to F* that +``factorial`` is to be erased during compilation, and the F* +type-and-effect checker ensures that ``Tot`` computation cannot depend +on an application of ``factorial n``. + +.. [#] The name ``GTot`` is meant to stand for "Ghost and Total" + computations, and is pronounced "gee tote". However, it's a + poor name and is far from self-explanatory. We plan to change + the name of this effect in the future (e.g., to something like + ``Spec``, ``Ghost``, or ``Erased``), though this is a breaking + change to a large amount of existing F* code. + +Ghost Computations as Specifications +------------------------------------ + +A ghost function like ``factorial`` can be used in specifications, +e.g., in a proof that a tail recursion optimization ``factorial_tail`` +is equivalent to ``factorial``. + +.. literalinclude:: ../code/FactorialTailRec.fst + :language: fstar + :start-after: //SNIPPET_START: factorial_tail$ + :end-before: //SNIPPET_END: factorial_tail$ + +This type allows a client to use the more efficient ``fact``, but for +reasoning purposes, one can use the more canonical ``factorial``, +proven equivalent to ``fact``. + +In contrast, if we were to try to implement the same specification by +directly using the factorial ghost function, F* complains with a +effect incompatibility error. + +.. literalinclude:: ../code/FactorialTailRec.fst + :language: fstar + :start-after: //SNIPPET_START: factorial_bad$ + :end-before: //SNIPPET_END: factorial_bad$ + +The error is: + +.. code-block:: none + + Computed type "r: nat{r == out * factorial n}" and + effect "GTot" is not compatible with the annotated + type "r: nat{r == out * factorial n}" effect "Tot" + +So, while F* forbids using ghost computations in ``Tot`` contexts, it +seems to be fine with accepting a use of factorial in specifications, +e.g., in the type ``r:nat { r == out * factorial n }``. We'll see in a +moment why this is permitted. + +Erasable and Non-informative Types +---------------------------------- + +In addition to using the ``GTot`` effect to classifies computations +that must be erased, F* also provides a way to mark certain *value +types* as erasable. + +Consider introducing an inductive type definition that is meant to +describe a proof term only and for that proof term to introduce no +runtime overhead. In a system like Coq, the type of Coq propositions +``Prop`` serves this purpose, but ``prop`` in F* is quite +different. Instead, F* allows an inductive type definition to be +marked as ``erasable``. + +For example, when we looked at the :ref:`simply typed lambda calculus +(STLC) `, we introduced the inductive type below, +to represent a typing derivation for an STLC term. One could define a +typechecker for STLC and give it the type shown below to prove it +correct: + +.. code-block:: fstar + + val check (g:env) (e:exp) : (t : typ & typing g e t) + +However, this function returns both the type ``t:typ`` computed for +``e``, we well as the typing derivation. Although the typing +derivation may be useful in some cases, often returning the whole +derivation is unnecessary. By marking the definition of the ``typing`` +inductive as shown below (and keeping the rest of the definition the +same), F* guarantees that the compiler will extract ``typing g e t`` +to the ``unit`` type and correspondinly, all values of ``typing g e +t`` will be erased to the unit value ``()`` + +.. code-block:: fstar + + [@@erasable] + noeq + type typing : env -> exp -> typ -> Type = ... + +Marking a type with the ``erasable`` attribute and having it be erased +to ``unit`` is safe because F* restricts how ``erasable`` types can be +used. In particular, no ``Tot`` computations should be able to extract +information from a value of an erasable type. + +Closely related to erasable types are a class of types that are called +*non-informative*, defined inductively as follows: + + 1. The type ``Type`` is non-informative + + 2. The type ``prop`` is non-informative (i.e., unit and all its + subtypes) + + 3. An erasable type is non-informative + + 4. A function type ``x:t -> Tot s`` is non-informative, if ``s`` is + non-informative + + 5. A ghost function type ``x:t -> GTot s`` is non-informative + + 6. A function type ``x:t -> C``, with user-defined computation type + ``C``, is non-informative if the effect label of ``C`` has the + erasable attribute. + +Intuitively, a non-informative type is a type that cannot be +case-analyzed in a ``Tot`` context. + +With this notion of non-informative types, we can now define the +restrictions on an ``erasable`` type: + + 1. Any computation that pattern matches on an erasable type must + return a non-informative type. + + 2. Inductive types with the ``erasable`` attribute do not support + built-in decidable equality and must also be marked ``noeq``. + + +The `erased` type, `reveal`, and `hide` +--------------------------------------- + +The ``erasable`` attribute can only be added to new inductive type +definitions and every instance of that type becomes erasable. If you +have a type like ``nat``, which is not erasable, but some occurrences +of it (e.g., in the arguments to ``Vector.append``) need to be erased, +the F* standard library ``FStar.Ghost.fsti`` offers the following: + +.. code-block:: fstar + + (** [erased t] is the computationally irrelevant counterpart of [t] *) + [@@ erasable] + val erased (t:Type u#a) : Type u#a + +``FStar.Ghost`` also offers a pair of functions, ``reveal`` and +``hide``, that form a bijection between ``a`` and ``erased a``. + +.. code-block:: fstar + + val reveal (#a: Type u#a) (v:erased a) : GTot a + + val hide (#a: Type u#a) (v:a) : Tot (erased a) + + val hide_reveal (#a: Type) (x: erased a) + : Lemma (ensures (hide (reveal x) == x)) + [SMTPat (reveal x)] + + val reveal_hide (#a: Type) (x: a) + : Lemma (ensures (reveal (hide x) == x)) + [SMTPat (hide x)] + +Importantly, ``reveal v`` breaks the abstraction of ``v:erased a`` +returning just an ``a``, but doing so incurs a ``GTot`` effect---so, +``reveal`` cannot be used in an arbitrary ``Tot`` contexts. + +Dually, ``hide v`` can be used to erase ``v:a``, since a ``Tot`` +context cannot depend on the value of an ``erased a``. + +The SMT patterns on the two lemmas allow F* and Z3 to automatically +instantiate the lemmas to relate a value and its hidden +counterpart---:ref:`this chapter ` provides more details on +how SMT patterns work. + +**Implicit coercions** + +``FStar.Ghost.erased``, ``reveal``, and ``hide`` are so commonly used +in F* that the compiler provides some special support for it. In +particular, when a term ``v:t`` is used in a context that expects an +``erased t``, F* implictly coerces ``v`` to ``hide v``. Likewise, when +the context expects a ``t`` where ``v:erased t`` is provided, F* +implicitly coerces ``v`` to ``reveal v``. + +The following examples illustrates a few usages and limitations. You +can ask F* to print the code with implicits enabled by using +``--dump_module RevealHideCoercions --print_implicits``. + +.. literalinclude:: ../code/RevealHideCoercions.fst + :language: fstar + +A few comments on these examples: + +* The first two functions illustrate how a ``nat`` is coerced + implicitly to ``erased nat``. Note, the effect of ``auto_reveal`` is + ``GTot`` + +* ``auto_reveal_2`` fails, since the the annotation claims, + incorrectly, that the effect label is ``Tot`` + +* ``incr`` is just a ``nat -> nat`` function. + +* ``incr_e`` is interesting because it calls ``incr`` with an + ``erased nat`` and the annotation expects an ``erased nat`` + too. The body of ``incr_e`` is implicitly coerced to ``hide (incr + (reveal x))`` + +* ``incr'`` is interesting, since it calls ``incr_e``: its body + is implicitly coerced to ``reveal (incr_e (hide x))`` + +* Finally, ``poly`` shows the limitations of implicit coercion: F* + only inserts coercions when the expected type of the term in a + context and the type of the term differ by an ``erased`` + constructor. In ``poly``, since ``==`` is polymorphic, the expected + type of the context is just an unresolved unification variable and, + so, no coercion is inserted. Instead, F* complains that ``y`` has + type ``erased nat`` when the type ``nat`` was expected. + + +Using Ghost Computations in Total Contexts +------------------------------------------ + +We have already noted that ``Tot < GTot``, enabling ``Tot`` +computations to be re-used in ``GTot`` contexts. For erasure to be +sound, it is crucial that ``GTot`` terms cannot be used in ``Tot`` +contexts, and indeed, F* forbids this in general. However, there are +two exceptions. + + +Effect Promotion for Non-informative Types +.......................................... + +Consider a term ``f`` with type ``GTot s``, where ``s`` is a +non-informative type. Since ``s`` is non-informative, no total context +can extract any information from ``f``. As such, F* allows implicitly +promoting ``GTot s`` to ``Tot s``, when ``s`` is a non-informative +type. + +For instance, the following is derivable, +``hide (factorial 0) : Tot (erased nat)``: let's work through it in detail. + +1. We know that that ``factorial n : GTot nat`` + +2. Recall from the discussion on :ref:`evaluation order + ` and the application of functions to + effectful arguments, ``hide (factorial 0)`` is equivalent to ``let + x = factorial 0 in hide x``, where ``x:nat`` and + ``hide x : Tot (erased nat)``. + +3. From the rule for sequential composition of effectful terms, the + type of ``let x = factorial 0 in hide x`` should be ``GTot (erased + nat)``, since ``GTot = lub GTot Tot``. + +4. Since ``erased nat`` is a non-informative type, ``GTot (erased + nat)`` is promoted to ``Tot (erased nat)``, which is then the type + of ``hide (factorial 0)``. + + +Effect promotion for ghost functions returning non-informative types +is very useful. It allows one to mix ghost computations with total +computations, so long as the result of the ghost sub-computation is +hidden with an erased type. For instance, in the code below, we use +``hide (factorial (n - 1))`` and use the result ``f_n_1`` in an +assertion to or some other proof step, all within a function that is +in the ``Tot`` effect. + +.. literalinclude:: ../code/FactorialTailRec.fst + :language: fstar + :start-after: //SNIPPET_START: factorial_tail_alt$ + :end-before: //SNIPPET_END: factorial_tail_alt$ + + +FStar.Ghost.Pull.pull +..................... + +Consider applying ``map factorial [0;1;2]``, where ``map : (a -> +Tot b) -> list a -> list b``. F* refuses to typecheck this written, +since ``factorial : nat -> GTot nat``, which is not a subtype of ``nat +-> Tot nat``. + +Using effect promotion, one could write ``map (fun x -> hide +(factorial x)) [0;1;2]``, but this has type ``list (erased nat)``, +which is not the same as ``list nat``. + +This is unfortunate since it seems that basic libraries designed for +use with with higher-order total functions cannot be reused with ghost +functions. [#]_ + +The library function ``FStar.Ghost.Pull.pull`` can help in such +situatinos. It has the following signature: + +.. code-block:: fstar + + val pull (#a:Type) (#b:a -> Type) (f: (x:a -> GTot (b x))) + : GTot (x:a -> b x) + + val pull_equiv (#a:Type) (#b:a -> Type) (f: (x:a -> GTot (b x))) (x:a) + : Lemma (ensures pull f x == f x) + [SMTPat (pull f x)] + + +This type states that for any ghost function ``f``, we can exhibit a +total function ``g`` with the same type as ``f``, while the lemma +``pull_equiv`` states that ``g`` is pointwise equal to ``f``. [#]_ However, +it may not be possible, in general, to compute ``g`` in a way that +enables it to be compiled by F*. So, ``pull f`` itself has ghost +effect, indicating that applications of ``pull`` cannot be used in +compilable code. + +Using ``pull``, one can write ``map (pull factorial) [0;1;2] : GTot +(list nat)``, thereby reusing ghost functions where total functions +are expected. + +.. [#] F* does not have any built-in support for effect-label + polymorphism. That is, one cannot write a type like this: ``map + #a #b #e (f: a -> e b) (l:list a) : e (list b)``, where ``e`` + is an effect variable. However, using F*'s dependent types, one + can code up various `ad hoc forms of effect polymorphism + <../code/AdHocEffectPolymorphism.fst>`_. Some other languages + with effect systems, notably `Koka + `_, do support + effect polymorphism primitively. + +.. [#] The ``SMTPat`` annotation on the ``pull_equiv`` lemma instructs + F* to allow Z3 to instantiate this lemma automatically. You + can learn more :ref:`about SMT patterns and quantifier + instantiation in a later chapter `. + +Revisiting Vector Concatenation +------------------------------- + +We now have all the ingredients to understand how the vector append +example shown at the start of this chapter works. Here, below, is a +version of the same code with all the implicit arguments and +reveal/hide operations made explicit. + +.. literalinclude:: ../code/VecErasedExplicit.fst + :language: fstar + +**Definition of vec** + +In the definition of the inductive type ``vec a``, we have two +occurrences of ``reveal``. Consider ``vec a (reveal n)``, the type of +the ``tl`` of the vector. ``reveal n`` is a ghost computation of type +``GTot nat``, so ``vec a (reveal n) : GTot Type``. But, since ``Type`` +is non-informative, ``GTot Type`` is promoted to ``Tot Type``. The +promotion from ``GTot Type`` to ``Tot Type`` is pervasive in F* and +enables ghost computations to be freely used in types and other +specifications. + +The ``vec a (reveal n + 1)`` in the result type of ``Cons`` is +similar. Here ``reveal n + 1`` has type ``GTot nat``, but applying it +to ``vec a`` produces a ``GTot Type``, which is promoted to ``Tot +Type``. + +**Type of append** + +The type of ``append`` has four occurrences of ``reveal``. Three of +them, in the type of ``v0``, ``v1``, and the return type behave the +same as the typing the fields of ``Cons``: the ``GTot Type`` is +promoted to ``Tot Type``. + +One additional wrinkle is in the decreases clause, where we have an +explicit ``reveal n``, since what decreases on each recursive call is +the ``nat`` that's in bijection with the parameter ``n``, rather than +``n`` itself. When F* infers a decreases clause for a function, any +erased terms in the clause are automatically revealed. + +**Definition of append** + +The recursive call instantiates the index parameters to ``n_tl`` and +``m``, which are both erased. + +When constructing the ``Cons`` node, its index argument is +instantiated to ``hide (reveal n_tl + reveal m)``. The needless +addition is marked with a ``hide`` enabling that F* compiler to erase +it. As we saw before in ``factorial_tail_alt``, using ``hide`` allows +one to mingle ghost computations (like ``(reveal n - 1)``) with total +computations, as needed for specifications and proofs. + +All of this is painfully explicit, but the implicit reveal/hide +coercions inserted by F* go a long way towards make things relatively +smooth. diff --git a/book/part4/part4_pure.rst b/book/part4/part4_pure.rst new file mode 100644 index 00000000000..335ebc30a8f --- /dev/null +++ b/book/part4/part4_pure.rst @@ -0,0 +1,794 @@ +.. _Part4_Pure: + +Primitive Effect Refinements +============================== + +.. note:: + + This chapter provides some background on Floyd-Hoare logic and + weakest-precondition-based verification condition generation. This + is necessary if you want to understand a bit about how F* infers + the logical constraints needed to prove the correctness of a + program. It is also useful background for more advanced material in + subsequent chapters about defining custom effects in F*, e.g., + effects to model state, exceptions, or concurrency. + +Refinement types ``x:t{p}`` *refine* value types ``t`` and allow us to +make more precise assertions about the values in the program. For +example, when we have ``v : x:int{x >= 0}``, then not only we know +that ``v`` is an ``int``, but also that ``v >= 0``. + +In a similar manner, F* allows refining computation types with +specifications that describe some aspects of a program's computational +behavior. These *effect refinements* can, in general, be defined by +the user in a reasoning system of their choosing, e.g., the +refinements may use separation logic, or they may count computation +steps. + +However, F* has built-in support for refining the specification of +pure programs with effect refinements that encode the standard +reasoning principles of Floyd-Hoare Logic and weakest +precondition-based calculi. Foreshadowing what's about to come in this +chapter, we can write the following specification for the +``factorial`` function: + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: factorial$ + :end-before: //SNIPPET_END: factorial$ + +Intuitively, this type states that ``factorial x`` is a computation +defined only when ``x >= 0`` and always terminates returning a value +``r >= 1``. In a way, this type is closely related to other, more +familiar, types we have given to ``factorial`` so far, e.g., ``nat -> +pos``, and, indeed, ``factorial`` can be used at this type. + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: fact$ + :end-before: //SNIPPET_END: fact$ + +Actually, in all the code we've seen so far, what's happening under +the covers is that F* infers a type for a pure program similar to +``Pure t pre post`` and then checks that that type can be subsumed to +a user-provided specification of the form ``Tot t'``. + +In this chapter, we look into how these ``Pure`` specifications work, +starting with a primer on Floyd-Hoare Logic and weakest precondition +calculi. If the reader is familiar with these, they may safely skip +the next subsections, though even if you are an expert, if may be of +interest to see how such program logics can be formalized in F*. + + +A Primer on Floyd-Hoare Logic and Weakest Preconditions +------------------------------------------------------- + +Floyd-Hoare Logic is a system of specifications and rules to reason +about the logical properties of programs, introduced by Robert Floyd +in a paper titled `Assigning Meaning to Programs +`_ and +by Tony Hoare in `An axiomatic basis for computer programming +`_. The notation used in +most modern presentations (called Hoare triples) is due to Hoare. An +algorithm to compute Hoare triples was developed by Edsger Dijkstra +`presented first in this paper +`_ , using a +technique called *weakest preconditions*. All of them received Turing +Awards for their work on these and other related topics. + +For an introduction to these ideas, we'll develop a small imperative +language with global variables, presenting + +* An operational semantics for the language, formalized as an + interpreter. + +* A Floyd-Hoare program logic proven sound with respect to the + operational semantics. + +* And, finally, an algorithm to compute weakest preconditions proved + sound against the Floyd-Hoare logic. + +Our language has the following abstract syntax: + + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: syntax$ + :end-before: //SNIPPET_END: syntax$ + +Expressions includes integer constants, global variables (represented +just as natural numbers), and some other forms, e.g., arithmetic +expressions like addition. + +A program includes: + +* Assignments, ``EAssign x e``, representing the assignment of the + result of an expression ``e`` to a global variable ``x``, i.e., ``x := e`` + +* ``Seq``, to compose programs sequentially + +* ``If`` to compose programs conditionally + +* And ``Repeat n p``, which represents a construct similar to a + ``for``-loop (or primitive recursion), where the program ``p`` is + repeated ``n`` times, where ``n`` evaluates to a non-negative + integer. + +Our language does not have ``while`` loops, whose semantics are a bit +more subtle to develop. We will look at a semantics for ``while`` in a +subsequent chapter. + +Operational Semantics +^^^^^^^^^^^^^^^^^^^^^ + +Our first step in giving a semantics to programs is to define an +interpreter for it to run a program while transforming a memory that +stores the values of the global variables. + +To model this memory, we use the type ``state`` shown below: + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: state$ + :end-before: //SNIPPET_END: state$ + +Writing a small evaluator for expressions is easy: + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: eval_expr$ + :end-before: //SNIPPET_END: eval_expr$ + +The interpreter for programs itself takes a bit more work, since +programs can both read and write the state. To structure our +interpreter, we'll introduce a simple state monad ``st a``. We've seen +this construction before in :ref:`a previous chapter +`---so, look there if the state monad is unfamiliar +to you. Recall that F* has support for monadic let operators: the +``let!`` provides syntactic sugar to convenient compose ``st`` terms. + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: monad$ + :end-before: //SNIPPET_END: monad$ + +Now, the interpreter itself is a total, recursive function ``run`` which +interprets a program ``p`` as a state-passing function of type ``st +unit``, or ``state -> unit & state``. + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: run$ + :end-before: //SNIPPET_END: run$ + +Let's look at its definition in detail: + + * ``Assign x e``: Evaluate ``e`` in the current state and then + update the state with a new value of ``x``. + + * ``Seq p1 p2``: Simply run ``p1`` and then run ``p2``, where + ``;!`` is syntactic sugar for ``let! _ = run p1 in run p2``. + + * ``If e p1 p2``: Evaluate ``e`` in the current state, branch on + its result and run either ``p1`` or ``p2`` + + * ``Repeat e p``: Evaluate ``e`` to ``n``, and if ``n`` is + greater than zero, call the mutually recursive ``run_repeat n + p``. Most of the subtlety here is in convincing F* that this + mutually recursive function terminates, but this is fairly + straightforward once you know how---we discussed + :ref:`termination proofs for mutually recursive functions earlier + `. + +These operational semantics are the ground truth for our programming +language---it defines how programs execute. Now that we have that +settled, we can look at how a Floyd-Hoare logic makes it possible to +reason about programs in a structured way. + +Floyd-Hoare Logic +^^^^^^^^^^^^^^^^^ + +The goal of a Floyd-Hoare logic is to provide a way to reason about a +program based on the structure of its syntax, rather than reasoning +directly about its operational semantics. The unit of reasoning is +called a *Hoare triple*, a predicate of the form ``{P} c {Q}``, where +``P`` and ``Q`` are predicates about the state of the program, and +``c`` is the program itself. + +We can *define* Hoare triples for our language by interpreting them as +an assertion about the operational semantics, where ``triple p c q`` +represents, formally, the Hoare triple ``{ p } c { q }``. + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: triple$ + :end-before: //SNIPPET_END: triple$ + +The predicate ``triple p c q`` is valid, if when executing ``c`` in a +state that satisfies ``p`` results in a state that satisfies ``q``. +The predicates ``p`` and ``q`` are also called precondition and +postcondition of ``c``, respectively. + +For each syntactic construct of our language, we can prove a lemma +that shows how to build an instance of the ``triple`` predicate for +that construct. Then, to build a proof of program, one stitches +together these lemmas to obtain a ``triple p main q``, a statement of +correctess of the ``main`` program. + +Assignment +++++++++++ + +Our first rule is for reasoning about variable assignment: + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: assignment$ + :end-before: //SNIPPET_END: assignment$ + +This lemma says that ``post`` holds after executing ``x := e`` in the +initial state ``s0``, if ``post`` holds on the initial state updated +at ``x`` with the value of ``e``. + +For example, to prove that after executing ``z := y + 1`` in ``s0``, +if we expect the value of ``z`` to be greater than zero`, then the +assignment rule says that ``read s0 y + 1 > 0`` should hold before the +assignment, which is what we would expect. + +Sequence +++++++++ + +Our next lemma about triples stitches together triples for two +programs that are sequentially composed: + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: sequence$ + :end-before: //SNIPPET_END: sequence$ + +The lemma says that if we can derive the Hoare triples of the two +statements such that postcondition of ``p1`` matches the precondition +of ``p2``, then we can compose them. + + +Conditional ++++++++++++ + +The lemma for conditionals is next: + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: conditional$ + :end-before: //SNIPPET_END: conditional$ + +It says that to derive the postcondition ``post`` from the ``If e p1 +p2``, we should be able to derive it from each of the branches with the +same precondition ``pre``. In addition, since we know that ``p1`` +executes only when ``e`` is non-zero, we can add these facts to the +preconditions of each branch. + +Repeat +++++++ + +In all the cases so far, these lemmas are proved automated by F* and +Z3. In the case of repeats, however, we need to do a little more work, +since an inductive argument is involved. + +The rule for ``repeat`` requires a *loop invariant* ``inv``. The loop +invariant is an assertion that holds before the loop starts, is +maintained by each iteration of the loop, and is provided as the +postcondition of the loop. + +The lemma below states that if we can prove that ``triple inv p inv``, +then we can also prove ``triple inv (Repeat e p) inv``. + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: repeat$ + :end-before: //SNIPPET_END: repeat$ + +The auxiliary lemma ``repeat_n`` proves that ``run_repeat p n`` +preserves ``inv``, if ``p`` preserves ``inv``. + +To call this lemma from the main ``repeat`` lemma, we need to "get +our hands on" the initial state ``s0``, and the :ref:`syntactic sugar +to manipulate logical connectives ` makes this +possible. + + +Consequence ++++++++++++ + +The final lemma about our Hoare triples is called the rule of +consequence. It allows strengthening the precondition and weakening +the postcondition of a triple. + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: consequence$ + :end-before: //SNIPPET_END: consequence$ + +A precondition of a program is an obligation before the statement +is executed. So if ``p`` requires ``pre``, we can always strengthen +the precondition to ``pre'``, provided ``pre' ==> pre``, i.e. it is +logically valid to require more than necessary in the +precondition. Similarly, postcondition is what a statement +guarantees. So if ``p`` guarantees ``post``, we can always weaken it +to guarantee less, i.e. some ``post'`` where ``post ==> post'``. + +Weakest Preconditions +^^^^^^^^^^^^^^^^^^^^^ + +The rules of Floyd-Hoare logic provide an abstract way to reason about +programs. However, the rules of the logic are presented +declaratively. For example, to apply the ``sequence`` rule, one has +derive triples for each component in a way that they prove exactly the +same assertion (``pre_mid``) about the intermediate state. There may +be many ways to do this, e.g., one could apply the rule of +consequence to weaken the postcondition of the first component, or to +strengthen the precondition of the second component. + +Dijkstra's system of weakest preconditions eliminates such ambiguity +and provides an *algorithm* for computing valid Hoare triples, +provided the invariants of all loops are given. This makes weakest +preconditions the basis of many program proof tools, since given a +program annotated with loop invariants, one can simply compute a +logical formula (called a verification condition) whose validity +implies the correctness of the program. + +At the core of the approach is a function ``WP (c, Q)``, which +computes a unique, weakest precondition ``P`` for the program ``c`` +and postcondition ``Q``. The semantics of ``WP`` is that ``WP (c, Q)`` +is the weakest precondition that should hold before executing ``c`` +for the postcondition ``Q`` to be valid after executing ``c``. Thus, +the function ``WP`` assigns meaning to programs as a transformer of +postconditions ``Q`` to preconditions ``WP (s, Q)``. + +The ``wp`` function for our small imperative language is shown below: + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: wp$ + :end-before: //SNIPPET_END: wp$ + +* The case of ``Assign`` is identical to the ``assignment`` lemma + shown earlier. + +* The case of ``Seq`` sequentially composes the wp's. That is, to + prove the ``post`` after running ``p1 ;; p2`` we need to prove ``wp + p2 post`` after running ``p1``. It may be helpful to read this case + as the equivalent form ``fun s0 -> wp p1 (fun s1 -> wp p2 post s1) + s0``, where ``s0`` is the initial state and ``s1`` is the state that + results after running just ``p1``. + +* The ``If`` case computes the WPs for each branch and requires them + to be proven under the suitable branch condition. + +* The ``Repeat`` case is most interesting: it involves an + existentially quantified invariant ``inv``, which is the loop + invariant. That is, to reason about ``Repeat n p``, one has to + somehow find an invariant ``inv`` that is true initially, and + implies both the WP of the loop body as well as the final + postcondition. + +The ``wp`` function is sound in the sense that it computes a +sufficient precondition, as proven by the following lemma. + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: wp_soundness$ + :end-before: //SNIPPET_END: wp_soundness$ + +One could also prove that ``wp`` computes the weakest precondition, +i.e., if ``triple p c q`` then ``forall s. p s ==> wp c q s``, though +we do not prove that formally here. + +A Sample Program Proof +^^^^^^^^^^^^^^^^^^^^^^ + +We now illustrate some sample proofs using our Hoare triples and +``wp`` function. To emphasize that Hoare triples provide an *abstract* +way of reasoning about the execution of programs, we define the +``hoare p c q`` an alias for ``triple p c q`` marked with an attribute +to ensure that F* and Z3 cannot reason directly about the underlying +definition of ``triple``---that would allow Z3 to find proofs by +reasoning about the operational semantics directly, which we want to +avoid , since it would not scale to larger programs. For more about +the ``opaque_to_smt`` and ``reveal_opaque`` construct, please see +:ref:`this section on opaque definitions `. + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: hoare$ + :end-before: //SNIPPET_END: hoare$ + +The lemmas above are just restatements of the ``wp_soundness`` and +``consequence`` lemmas that we've already proven. Now, these are the +only two lemmas we have to reason about the ``hoare p c q`` predicate. + +Next, we define some notation to make it a bit more convenient to +write programs in our small language. + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: notation$ + :end-before: //SNIPPET_END: notation$ + +Finally, we can build proofs of some simple, loop-free programs +automatically by computing their ``wp`` using ``wp_hoare`` and +applying ``hoare_consequence`` to get F* and Z3 to prove that the +inferred WP is implied by the annotated precondition. + +.. literalinclude:: ../code/Imp.fst + :language: fstar + :start-after: //SNIPPET_START: swap$ + :end-before: //SNIPPET_END: swap$ + +This recipe of computing verification conditions using WPs and then +checking the computed WP against the annotated specification using a +solver like Z3 is a very common and powerful pattern. In fact, as +we'll see below, the methodology that we've developed here for our +small imperative language is exactly what the F* typechecker does (at +a larger scale and for the whole F* language) when checking an F* +program. + + +The ``PURE`` Effect: A Dijkstra Monad for Pure Computations +------------------------------------------------------------ + +F* provides a weakest precondition calculus for reasoning about pure +computations. The calculus is based on a *Dijkstra Monad*, a +construction first introduced in `this paper +`_. In +this chapter, we will learn about Dijkstra Monad and its usage in +specifying and proving pure programs in F*. + +The first main difference in adapting the Hoare triples and weakest +precondition computations that we saw earlier to the setting of F*'s +functional language is that there are no global variables or mutable +state (we'll see about how model mutable state in F*'s effect system +later). Instead, each pure expression in F*'s *returns* a value and +the postconditions that we will manipulate are predicates about these +values, rather than state predicates. + +To illustrate, we sketch the definition of pure WPs below. + +.. code-block:: none + + WP c Q = Q c + WP (let x = e1 in e2) Q = WP e1 (fun x -> WP e2 Q) + WP (if e then e1 else e2) Q = (e ==> WP e1 Q) /\ (~e ==> WP e2 Q) + +* The WP of a constant ``c`` is just the postcondition ``Q`` applied to ``c``. + +* The WP of a ``let`` binding is a sequential composition of WPs, + applied to the *values* returned by each sub-expression + +* The WP of a condition is the WP of each branch, weakened by the + suitable branch condition, as before. + +The F* type system internalizes and generalizes this WP construction +to apply it to all F* terms. The form this takes is as a computation +type in F*, ``PURE a wp``, where in ``prims.fst``, ``PURE`` is defined +as an F* primitive effect with a signature as shown below--we'll see +much more of the ``new_effect`` syntax as we look at user-defined +effects in subsequent chapters; for now, just see it as a reserved +syntax in F* to introduce a computation type constructor. + +.. code-block:: fstar + + new_effect PURE (a:Type) (w:wp a) { ... } + +where + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: wp$ + :end-before: //SNIPPET_END: wp$ + +A program ``e`` of type ``PURE a wp`` is a computation which + + * Is defined only when ``wp (fun _ -> True)`` is valid + + * If ``wp post`` is valid, then ``e`` terminates without any side + effects and returns a value ``v:a`` satisfying ``post v``. + +Notice that ``wp a`` is the type of a function transforming +postconditions (``a -> Type0``) to preconditions (``Type0``). [#]_ The +``wp`` argument is also called an *index* of the ``PURE`` effect. [#]_ + +The return operator for ``wp a`` is shown below: it is analogous to +the ``WP c Q`` and ``WP x Q`` rules for variables and constants that +we showed earlier: + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: return_wp$ + :end-before: //SNIPPET_END: return_wp$ + +The bind operator for ``wp a`` is analogous to the rule for sequencing +WPs, i.e., the rule for ``WP (let x = e1 in e2) Q`` above: + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: bind_wp$ + :end-before: //SNIPPET_END: bind_wp$ + +Finally, analogous to the WP rule for conditionals, one can write a +combinator for composing ``wp a`` in a branch: + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: if_then_else_wp$ + :end-before: //SNIPPET_END: if_then_else_wp$ + + +This is the essence of the Dijkstra monad construction for pure +programs: the rule for computing weakest preconditions for a +computation *returning* a value ``x`` is ``return_wp``; the rule for +computing the WP of the sequential composition of terms is the +sequential composition of WPs using ``bind_wp``; the rule for +computing the WP of a conditional term is the conditional composition +of WPs using ``if_then_else_wp``. + +If fact, if one thinks of pure computations as the identity monad, +``tot a`` as shown below: + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: tot$ + :end-before: //SNIPPET_END: tot$ + +then the parallel between the ``tot`` monad and ``wp`` becomes even +clearer---the WP analog of ``return_tot`` is ``return_wp`` and of +``bind_tot`` is ``bind_wp``. + +It turns out that ``wp a`` (for monotonic weakest preconditions) is +itself a monad, as shown below by a proof of the monad laws: + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: mwp_laws$ + :end-before: //SNIPPET_END: mwp_laws$ + + +.. [#] It is also possible to define ``post a = a -> prop`` and ``pre + = prop``. However, the F* libraries for pure WPs using + ``Type0`` instead of ``prop``, so we remain faithful to that + here. + +.. [#] Dijkstra monads are also related to the continuation + monad. Continuation monad models `Continuation Passing Style + `_ + programming, where the control is passed to the callee + explicitly in the form of a continuation. For a result type + ``r``, the continuation monad is defined as follows: + + .. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: cont$ + :end-before: //SNIPPET_END: cont$ + + If we squint a bit, we can see that the ``wp`` monad we defined + earlier, is nothing but a continuation into ``Type0``, i.e., + ``wp a = cont Type0 a`` (or ``cont prop a``, if one prefers to + use ``prop``). + +``PURE`` and ``Tot`` +--------------------- + +When typechecking a program, F* computes a weakest precondition which +characterizes a necessary condition for the program to satisfy all its +typing constraints. This computed weakest precondition is usually +hidden from the programmer, but if you annotate your program suitably, +you can get access to it, as shown in the code snippet below: + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: square$ + :end-before: //SNIPPET_END: square$ + +The type says that ``square n`` is a pure function, which for any +postcondition ``q:nat -> prop``, + + * Is defined only when ``n * n >= 0`` and when ``q (n * n)`` is valid + + * And returns a value ``m:nat`` satisfying ``q m`` + +Let's look at another example: + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: maybe_incr$ + :end-before: //SNIPPET_END: maybe_incr$ + +Notice how the ``wp`` index of ``PURE`` mirrors the structure of the +computation itself---it starts with an ``if_then_else_wp``, then in +the first branch, uses a ``bind_wp`` followed by a return; and in the +else branch it returns ``x``. + +As such, the wp-index simply "lifts" the computation into a +specification in a form amenable to logical reasoning, e.g., using the +SMT solver. For pure programs this may seem like overkill, since the +pure term itself can be reasoned about directly, but when the term +contains non-trivial typing constraints, e.g., such as those that +arise from refinement type checking, lifting the entire program into a +single constraint structures and simplifies logical reasoning. + +Of course, one often writes specifications that are more abstract than +the full logical lifting of the program, as in the example below, +which says that to prove ``post`` of the return value, the +precondition is to prove ``post`` on all ``y >= x``. This is a +valid, although weaker, characterization of the function's return +value. + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: maybe_incr2$ + :end-before: //SNIPPET_END: maybe_incr2$ + +The ``PURE`` computation type comes with a built-in weakening rule. In +particular, if a term is computed to have type ``PURE a wp_a`` and it is +annotated to have type ``PURE b wp_b``, then F* does the following: + + 1. It computes a constraint ``p : a -> Type0``, which is sufficient + to prove that ``a`` is a subtype of ``b``, e.g., is ``a = int`` + and ``b = nat``, the constraint ``p`` is ``fun (x:int) -> x >= + 0``. + + 2. Next, it strengthens ``wp_a`` to assert that the returned value + validates the subtyping constraints ``p x``, i.e., it builds + ``assert_wp wp_a p``, where + + .. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: assert_wp$ + :end-before: //SNIPPET_END: assert_wp$ + + 3. Finally, it produces the verification condition ``stronger_wp #b + wp_b (assert_wp wp_a p)``, where ``stronger_wp`` is defined as + shown below: + + .. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: stronger_wp$ + :end-before: //SNIPPET_END: stronger_wp$ + + That is, for any postcondition ``post``, the precondition ``wp_b + post`` implies the original precondition ``wp_a post`` as well as + the subtyping constraint ``p x``. This matches the intuition + about preconditions that we built earlier: it is always sound to + require more in the precondition. + +Thus, when we have ``e:PURE a wp`` in F*, the ``wp`` is *a* predicate +transformer for ``e``, not necessarily the weakest one. + +Of course, even ``maybe_incr2`` is not particularly idiomatic in +F*. One would usually annotate a program with a refinement type, such +as the one below: + +.. literalinclude:: ../code/Pure.fst + :language: fstar + :start-after: //SNIPPET_START: maybe_incr_tot$ + :end-before: //SNIPPET_END: maybe_incr_tot$ + +Internally to the compiler, F* treats ``Tot t`` as the following +instance of ``PURE``: + +.. code-block:: fstar + + Tot t = PURE t (fun post -> forall (x:t). post x) + +Once ``Tot t`` is viewed as just an instance of ``PURE``, checking if +a user annotation ``Tot t`` is stronger than the inferred +specification of a term ``PURE a wp`` is just as explained before. + +``Pure``: Hoare Triples for ``PURE`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Although specification are easier to *compute* using WPs, they are +more natural to read and write when presented as Hoare triples, with a +clear separation between precondition and postconditions. Further, +when specifications written as Hoare triples naturally induce +monotonic WPs. + +F* provides an effect abbreviation called ``Pure`` for writing and +typechecking Hoare-style specifications for pure programs, and is +defined as shown below in ``prims.fst``: + +.. code-block:: fstar + + effect Pure (a:Type) (req:Type0) (ens:a -> Type0) = + PURE a (fun post -> req /\ (forall x. ens x ==> post x)) + +The signature of ``Pure`` is ``Pure a req ens``, where ``req`` is the +precondition and ``ens:a -> Type0`` is the postcondition. Using +``Pure``, we can write the ``factorial`` function we saw at the top of +this chapter---F* infers a ``PURE a wp`` type for it, and relates it +to the annotated ``Pure int req ens`` type, proving that the latter +has a stronger precondition and weaker postcondition. + +One may wonder when one should write specifications using the notation +``x:a -> Pure b req ens`` versus ``x:a{req} -> Tot (y:b { ens y +})``. The two styles are closely related and choosing between them is +mostly a matter of taste. As you have seen, until this point in the +book, we have not used ``Pure a req ens`` at all. However, when a +function has many pre and postconditions, it is sometimes more +convenient to use the ``Pure a req ens`` notation, rather than +stuffing all the constraints in refinement types. + + +``GHOST`` and ``DIV`` +--------------------- + +Just as ``PURE`` is an wp-indexed refinement of ``Tot``, F* provides +two more primitive wp-indexed effects: + + * ``GHOST (a:Type) (w:wp a)`` is a refinement of ``GTot a`` + + * ``DIV (a:Type) (w:wp a)`` is a refinement of ``Dv a`` + +That is, F* uses the ``GHOST`` effect to infer total correctness WPs +for ghost computations, where, internally, ``GTot a`` is equivalent to +``GHOST a (fun post -> forall x. post x)`` + +Likewise, F* uses the ``DIV`` effect to infer *partial correctness* +WPs for potentially non-terminating computations, where, internally, +``Dv a`` is equivalent to ``DIV a (fun post -> forall x. post x)``. + +As with ``Tot`` and ``PURE``, F* automatically relates ``GTot`` and +``GHOST`` computations, and ``Dv`` and ``DIV`` computations. Further, +the effect ordering ``Tot < Dv`` and ``Tot < GTot`` extends to ``PURE +< DIV`` and ``PURE < GHOST`` as well. + +The ``prims.fst`` library also provides Hoare-triple style +abbreviations for ``GHOST`` and ``DIV``, i.e., + +.. code-block:: fstar + + effect Ghost a req ens = GHOST a (fun post -> req /\ (forall x. ens x /\ post x)) + effect Div a req ens = DIV a (fun post -> req /\ (forall x. ens x /\ post x)) + +These Hoare-style abbreviations are more convenient to use than their +more primitive WP-based counterparts. + +The tradeoffs of using ``Ghost`` vs. ``GTot`` or ``Div`` vs. ``Dv`` +are similar to those for ``Pure`` vs ``Tot``---it's mostly a matter of +taste. In fact, there are relatively few occurrences of ``Pure``, +``Ghost``, and ``Div`` in most F* codebases. However, there is one +important exception: ``Lemma``. + +The ``Lemma`` abbreviation +-------------------------- + +We can finally unveil the definition of the ``Lemma`` syntax, which we +introduced as a syntactic shorthand in :ref:`an early chapter +`. In fact, ``Lemma`` is defined in ``prims.fst`` +as follows: + +.. code-block:: fstar + + effect Lemma (a: eqtype_u) + (pre: Type) + (post: (squash pre -> Type)) + (smt_pats: list pattern) = + Pure a pre (fun r -> post ()) + +That is, ``Lemma`` is an instance of the Hoare-style refinement of +pure computations ``Pure a req ens``. So, when you write a proof term +and annotate it as ``e : Lemma (requires pre) (ensures post)``, F* +infers a specification for ``e : PURE a wp``, and then, as with all +PURE computations, F* tries to check that the annotated ``Lemma`` +specification has a stronger WP-specification than the computed +weakest precondition. + +Of course, F* still includes syntactic sugar for ``Lemma``, e.g., +``Lemma (requires pre) (ensures post)`` is desugared to ``Lemma unit +pre (fun _ -> post) []``. The last argument of a lemma, the +``smt_pats`` are used to introduce lemmas to the SMT solver for proof +automation---a :ref:`later chapter ` covers that in detail. + +Finally, notice the type of the ``post``, which assumes ``squash pre`` +as an argument--this is what allows the ``ensures`` clause of a +``Lemma`` to assume that what was specified in the ```requires`` +clause. + diff --git a/book/part4/part4_user_defined_effects.rst.outline b/book/part4/part4_user_defined_effects.rst.outline new file mode 100644 index 00000000000..b8537122fb3 --- /dev/null +++ b/book/part4/part4_user_defined_effects.rst.outline @@ -0,0 +1,53 @@ +.. _Part4_UDFX: + + +User-defined Effects +==================== + +General definitions, various classes of effects, substitutive etc. + +And then backed by many examples. + +A less formal, more code-centric version of the paper. + +I would expect this to be quite long, with several sub-sections. + +It would be nice for each section to introduce background on basic +constructions + +e.g., + +a section on graded monads + +a section on Dijkstra monads generalized from before, as a morphism between computational and spec monad + +a section on algebraic effects, with some background on what they're good for and how we model them + +etc. + + + + + +The primitive effects in F* provide a fixed specification and +reasoning mechanism for pure, ghost, and divergent +computations. :ref:`Earlier ` we also saw that using monads +we can model different kind of effects and specify their +semantics. For reasoning about effectful programs, however, such +semantic models may not be the right tool. Indeed several +monad-like abstractions have been proposed in the literature that are +suitable for different tasks. With user-defined effects, F* allows +building such custom abstractions and program logics, seamlessly +integrated with other features (recursion, inductive types, +...) and programmability using the same syntax that we have seen so +far. We turn our attention to user-defined effects next. + + + + + + + + + + diff --git a/book/part5/part5.rst b/book/part5/part5.rst new file mode 100644 index 00000000000..39393d0f3c8 --- /dev/null +++ b/book/part5/part5.rst @@ -0,0 +1,46 @@ +.. _Part5: + +######################################## +Tactics and Metaprogramming with Meta-F* +######################################## + +**This part of the book is still heavily under construction** + +So far, we have mostly relied on the SMT solver to do proofs in F*. +This works rather well: we got this far, after all! However, sometimes, +the SMT solver is really not able to complete our proof, or takes too +long to do so, or is not *robust* (i.e. works or fails due to seemingly +insignificant changes). + +This is what Meta-F* was originally designed for. It provides the +programmer with more control on how to break down a proof and guide +the SMT solver towards finding a proof by using *tactics*. Moreover, a +proof can be fully completed within Meta-F* without using the SMT +solver at all. This is the usual approach taken in other proof +assistants (such as Lean, Coq, or Agda), but it's not the preferred +route in F*: we will use the SMT for the things it can do well, and +mostly write tactics to "preprocess" obligations and make them easier +for the solver, thus reducing manual effort. + +Meta-F* also allows for *metaprogramming*, i.e. generating programs +(or types, or proofs, ...) automatically. This should not be +surprising to anyone already familiar with proof assistants and the +`Curry-Howard correspondence +`_. There +are however some slight differences between tactic-based proofs and +metaprogramming, and more so in F*, so we will first look at +automating proofs (i.e. tactics), and then turn to metaprogramming +(though we use the generic name "metaprogram" for tactics as well). + +In summary, when the SMT solver "just works" we usually do not bother +writing tactics, but, if not, we still have the ability to roll up our +sleeves and write explicit proofs. + +Speaking of rolling up our sleeves, let us do just that, and get our +first taste of tactics. + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + part5_meta diff --git a/book/part5/part5_meta.rst b/book/part5/part5_meta.rst new file mode 100644 index 00000000000..c4238f778c4 --- /dev/null +++ b/book/part5/part5_meta.rst @@ -0,0 +1,523 @@ +.. _MetaFStar_intro: + +An Overview of Tactics +====================== + +In this chapter, we quickly introduce several of the main concepts +underlying Meta-F* and its use in writing tactics for proof +automation. The goal is to get you quickly up to speed on basic uses +of tactics. Subsequent chapters will revisit the concepts covered here +in more detail, introduce more advanced aspects of Meta-F*, and show +them at use in several case studies. + + +Decorating assertions with tactics +---------------------------------- + +As you know already, F* verifies programs by computing verification +conditions (VCs) and calling an SMT solver (Z3) to prove them. Most +simple proof obligations are handled completely automatically by Z3, +and for more complex statements we can help the solver find a proof +via lemma calls and intermediate assertions. Even when using lemma +calls and assertions, the VC for a definition is sent to Z3 in one +single piece (though :ref:`SMT queries can be split via an option +`.). This "monolithic" style of proof can become +unwieldy rapidly, particularly when the solver is being pushed to its +limits. + +The first ability Meta-F* provides is allowing to attach specific +tactics to assertions. These tactics operate on the "goal" that we +want to prove, and can "massage" the assertion by simplifying it, +splitting it into several sub-goals, tweaking particular SMT options, +etc. + +For instance, let us take the the following example, where we want to +guarantee that ``pow2 x`` is less than one million given that ``x`` is +at most ``19``. One way of going about this proof is by noting that +``pow2`` is an increasing function, and that ``pow2 19`` is less than +one million, so we try to write something like this: + +.. literalinclude:: ../code/Part5.Pow2.fst + :language: fstar + :start-after: //SNIPPET_START: pow2_0 + :end-before: //SNIPPET_END: pow2_0 + +Sadly, this doesn't work. First of all, Z3 cannot automatically +prove that ``pow2`` is increasing, but that is to be expected. +We could prove this by a straightforward induction. However, we +only need this fact for ``x`` and ``19``, so we can simply call +``FStar.Math.Lemmas.pow2_le_compat`` from the library: + +.. literalinclude:: ../code/Part5.Pow2.fst + :language: fstar + :start-after: //SNIPPET_START: pow2_1 + :end-before: //SNIPPET_END: pow2_1 + +Now, the second assertion fails. Z3 will not, with the default fuel +limits, unfold pow2 enough times to compute ``pow2 19`` +precisely. (You can read more about how F* :ref:`uses "fuel" to +control the SMT solver's ability to unfold recursive definitions +`.) Here we will use our first call into Meta-F*: via +the ``by`` keyword, we can attach a tactic to an assertion. In this +case, we'll ask Meta-F* to ``compute()`` over the goal, simplifying as +much as it can via F*'s normalizer, like this: + +.. literalinclude:: ../code/Part5.Pow2.fst + :language: fstar + :start-after: //SNIPPET_START: pow2_2 + :end-before: //SNIPPET_END: pow2_2 + +Now the lemma verifies! Meta-F* reduced the proof obligation into a +trivial equality. Crucially, however, the ``pow2 19 == 524288`` shape is +kept as-is in the postcondition of the assertion, so we can make use of +it! If we were just to rewrite the assertion into ``524288 == 524288`` +that would not be useful at all. + +How can we know what Meta-F* is doing? We can use the ``dump`` tactic to +print the state of the proof after the call to ``compute()``. + +.. literalinclude:: ../code/Part5.Pow2.fst + :language: fstar + :start-after: //SNIPPET_START: pow2_3 + :end-before: //SNIPPET_END: pow2_3 + +With this version, you should see something like: + +.. code-block:: fstar + + Goal 1/1 + x: x: nat{x < 20} + p: pure_post unit + uu___: forall (pure_result: unit). pow2 x < 1000000 ==> p pure_result + pure_result: unit + uu___'0: pow2 x <= pow2 19 + -------------------------------------------------------------------------------- + squash (524288 == 524288) + (*?u144*) _ + +as output from F* (or in the goals buffer if you are using emacs with fstar-mode.el). +The ``print`` primitive can also be useful. + +A "goal" is some proof obligation that is yet to be solved. Meta-F* +allows you to capture goals (e.g. via ``assert..by``), modify them (such +as with ``compute``), and even to completely solve them. In this case, we +can solve the goal (without Z3) by calling ``trivial()``, a helper +tactic that discharges trivial goals (such as trivial equalities). + +.. literalinclude:: ../code/Part5.Pow2.fst + :language: fstar + :start-after: //SNIPPET_START: pow2_4 + :end-before: //SNIPPET_END: pow2_4 + +If you ``dump`` the state just after the ``trivial()`` call, you should +see no more goals remain (this is what ``qed()`` checks). + +.. note:: + + Meta-F* does not yet allow a fully interactive style of proof, and + hence we need to re-check the entire proof after every edit. We hope + to improve this soon. + +There is still the "rest" of the proof, namely that ``pow2 x < 1000000`` +given the hypothesis and the fact that the assertion holds. We call +this *skeleton* of the proof, and it is (by default) not handled by +Meta-F*. In general, we only use tactics on those assertions that are +particularly hard for the SMT solver, but leave all the rest to it. + +The ``Tac`` effect +------------------- + +.. note:: + + Although we have seen a bit about :ref:`monads and computational + effects in a previous chapter `, we have yet to fully + describe F*'s effect system. So, some of what follows may be a bit + confusing. However, you don't need to fully understand how the + ``Tac`` effect is implemented to use tactics. Feel free to skip + ahead, if this section doesn't make much sense to you. + +What, concretely, are tactics? So far we've written a few simple ones, +without too much attention to their structure. + +Tactics and metaprograms in F* are really just F* terms, but *in a +particular effect*, namely ``Tac``. To construct interesting +metaprograms, we have to use the set of *primitives* provided by Meta-F*. +Their full list is in the ``FStar.Tactics.Builtins`` module. +So far, we have actually not used any primitive directly, but +only *derived* metaprograms present in the standard library. + +Internally, ``Tac`` is implemented via a combination of 1) a state +monad, over a ``proofstate``, 2) exceptions and 3) divergence or +non-termination. The state monad is used to implicitly carry the +proofstate, without us manually having to handle all goals +explicitly. Exceptions are a useful way of doing error handling. Any +declared exception can be ``raise``'d within a metaprogram, and the +``try..with`` construct works exactly as for normal programs. There +are also ``fail``, ``catch`` and ``recover`` primitives. + +Metaprograms cannot be run directly. This is needed to retain the +soundness of pure computations, in the same way that stateful and +exception-raising computations are isolated from the ``Pure`` fragment +(and from each other). Metaprograms can only be used where F* expects +them , such as in an ``assert..by`` construct. Here, F* will run the +metaprogram on an initial proofstate consisting (usually) of a single +goal, and allow the metaprogram to modify it. + +To guarantee soundness, i.e., that metaprograms do not prove false +things, all of the primitives are designed to perform small and +correct modifications of the goals. Any metaprogram constructed from +them cannot do anything to the proofstate (which is abstract) except +modifying it via the primitives. + +Having divergence as part of the ``Tac`` effect may seem a bit odd, +since allowing for diverging terms usually implies that one can form a +proof of false, via a non-well-founded recursion. However, we should +note that this possible divergence happens at the *meta* level. If we +call a divergent tactic, F* will loop forever waiting for it to finish, +never actually accepting the assertion being checked. + +As you know, F* already has exceptions and divergence. All ``Dv`` and +``Ex`` functions can readily be used in Meta-F* metaprograms, as well as +all ``Tot`` and ``Pure`` functions. For instance, you can use all of the +``FStar.List.Tot`` module if your metaprogram uses lists. + +Goals +----- + +Essentially, a Meta-F* tactic manipulates a *proofstate*, which is +essentially a set of *goals*. Tactic primitives usually work on the +goals, for example by simplifying (like ``compute()``) or by breaking +them down into smaller *sub*-goals. + +When proving assertions, all of our goals will be of the shape ``squash +phi``, where ``phi`` is some logical formula we must prove. One way to +break down a goal into subparts is by using the ``mapply`` tactic, which +attempts to prove the goal by instantiating the given lemma or function, +perhaps adding subgoals for the hypothesis and arguments of the lemma. +This "working backwards" style is very common in tactics frameworks. + +For instance, we could have proved the assertion that ``pow2 x <= pow2 19`` +in the following way: + +.. code-block:: fstar + + assert (pow2 x <= pow2 19) by (mapply (`FStar.Math.Lemmas.pow2_le_compat)); + +This reduces the proof of ``pow2 x <= pow2 19`` to ``x <= 19`` (the +precondition of the lemma), which is trivially provably by Z3 in this +context. Note that we do not have to provide the arguments to the lemma: +they are inferred by F* through *unification*. In a nutshell, this means +F* finds there is an obvious instantiation of the arguments to make the +postcondition of the lemma and the current assertion coincide. When some +argument is *not* found via unification, Meta-F* will present a new goal +for it. + +This style of proof is more *surgical* than the one above, since the +proof that ``pow2 x <= pow2 19`` does not "leak" into the rest of the +function. If the proof of this assertion required several auxiliary +lemmas, or a tweak to the solver's options, etc, this kind of style can +pay off in robustness. + +Most tactics works on the *current* goal, which is the first one in +the proofstate. When a tactic reduces a goal ``g`` into ``g1,...,gn``, the +new ``g1,..,gn`` will (usually) be added to the beginning of the list of +goals. + +In the following simplified example, we are looking to prove ``s`` +from ``p`` given some lemmas. The first thing we do is apply the +``qr_s`` lemma, which gives us two subgoals, for ``q`` and ``r`` +respectively. We then need to proceed to solve the first goal for +``q``. In order to isolate the proofs of both goals, we can ``focus`` +on the current goal making all others temporarily invisible. To prove +``q``, we then just use the ``p_r`` lemma and obtain a subgoal for +``p``. This one we will just just leave to the SMT solver, hence we +call ``smt()`` to move it to the list of SMT goals. We prove ``r`` +similarly, using ``p_r``. + +.. literalinclude:: ../code/Part5.Mapply.fst + :language: fstar + :start-after: //SNIPPET_START: mapply + :end-before: //SNIPPET_END: mapply + +Once this tactic runs, we are left with SMT goals to prove ``p``, which Z3 +discharges immediately. + +Note that ``mapply`` works with lemmas that ensure an implication, or +that have a precondition (``requires``/``ensures``), and even those that take a +squashed proof as argument. Internally, ``mapply`` is implemented via the +``apply_lemma`` and ``apply`` primitives, but ideally you should not need to +use them directly. + +Note, also, that the proofs of each part are completely isolated from +each other. It is also possible to prove the ``p_gives_s`` lemma by calling +the sublemmas directly, and/or adding SMT patterns. While that style of +proof works, it can quickly become unwieldy. + +Quotations +---------- + +In the last few examples, you might have noted the backticks, such as +in ``(`FStar.Math.Lemmas.pow2_le_compat)``. This is a *quotation*: it +represents the *syntax* for this lemma instead of the lemma itself. It +is called a quotation since the idea is analogous to the word "sun" +being syntax representing the sun. + +A quotation always has type ``term``, an abstract type representing +the AST of F*. + +Meta-F* also provides *antiquotations*, which are a convenient way of +modifying an existing term. For instance, if ``t`` is a term, we can write +```(1 + `#t)`` to form the syntax of "adding 1" to ``t``. The part inside +the antiquotation (```#``) can be anything of type ``term``. + +Many metaprogramming primitives, however, do take a ``term`` as an +argument to use it in proof, like ``apply_lemma`` does. In this case, +the primitives will typecheck the term in order to use it in proofs +(to make sure that the syntax actually corresponds to a meaningful +well-typed F* term), though other primitives, such as +``term_to_string``, won't typecheck anything. + +We will see ahead that quotations are just a convenient way of +constructing syntax, instead of doing it step by step via ``pack``. + +Basic logic +----------- + +Meta-F* provides some predefined tactics to handle "logical" goals. + +For instance, to prove an implication ``p ==> q``, we can "introduce" the +hypothesis via ``implies_intro`` to obtain instead a goal for ``q`` in a +context that assumes ``p``. + +For experts in Coq and other provers, this tactic is simply called +``intro`` and creates a lambda abstraction. In F* this is slightly +more contrived due to squashed types, hence the need for an +``implies_intro`` different from the ``intro``, explained ahead, that +introduces a binder. + +Other basic logical tactics include: + + - ``forall_intro``: for a goal ``forall x. p``, introduce a fresh ``x`` into + the context and present a goal for ``p``. + + - ``l_intros``: introduce both implications and foralls as much as + possible. + + - ``split``: split a conjunction (``p /\ q``) into two goals + + - ``left``/``right``: prove a disjunction ``p \/ q`` by proving ``p`` or ``q`` + + - ``assumption``: prove the goal from a hypothesis in the context. + + - ``pose_lemma``: given a term ``t`` representing a lemma call, add its + postcondition to the context. If the lemma has a precondition, it is + presented as a separate goal. + +See the `FStar.Tactics.Logic +`_ +module for more. + +Normalizing and unfolding +------------------------- + +We have previously seen ``compute()``, which blasts a goal with F*'s +normalizer to reduce it into a *normal form*. We sometimes need a +bit more control than that, and hence there are several tactics to +normalize goals in different ways. Most of them are implemented via a +few configurable primitives (you can look up their definitions in the +standard library) + + - ``compute()``: calls the normalizer with almost all steps enabled + + - ``simpl()``: simplifies logical operations (e.g. reduces ``p /\ True`` + to ``p``). + + - ``whnf()`` (short for "weak head normal form"): reduces the goal + until its "head" is evident. + + - ``unfold_def `t``: unfolds the definition of the name ``t`` in the goal, + fully normalizing its body. + + - ``trivial()``: if the goal is trivial after normalization and simplification, + solve it. + +The ``norm`` primitive provides fine-grained control. Its type is +``list norm_step -> Tac unit``. The full list of ``norm_step`` s can +be found in the ``FStar.Pervasives`` module, and it is the same one +available for the ``norm`` marker in ``Pervasives`` (beware of the +name clash between ``Tactics.norm`` and ``Pervasives.norm``!). + +Inspecting and building syntax +------------------------------ + +As part of automating proofs, we often need to inspect the syntax of +the goal and the hypotheses in the context to decide what to do. For +instance, instead of blindly trying to apply the ``split`` tactic (and +recovering if it fails), we could instead look at the *shape* of the +goal and apply ``split`` only if the goal has the shape ``p1 /\ p2``. + +Note: inspecting syntax is, perhaps obviously, not something we can +just do everywhere. If a function was allowed to inspect the syntax of +its argument, it could behave differently on ``1+2`` and ``3``, which +is bad, since ``1+2 == 3`` in F*, and functions are expected to map +equal arguments to the same result. So, for the most part, we cannot +simply turn a value of type ``a`` into its syntax. Hence, quotations +are *static*, they simply represent the syntax of a term and one +cannot turn values into terms. There is a more powerful mechanism of +*dynamic quotations* that will be explained later, but suffice it to +say for now that this can only be done in the ``Tac`` effect. + +As an example, the ``cur_goal()`` tactic will return a value of type +``typ`` (an alias for ``term`` indicating that the term is really the +representation of an F* type) representing the syntax of the current +goal. + +The ``term`` type is *abstract*: it has no observable structure +itself. Think of it as an opaque "box" containing a term inside. A +priori, all that can be done with a ``term`` is pass it to primitives +that expect one, such as ``tc`` to type-check it or ``norm_term`` to +normalize it. But none of those give us full, programatic access to +the structure of the term. + + +That's where the ``term_view`` comes in: following `a classic idea +introduced by Phil Wadler +`_, there is function +called ``inspect`` that turns a ``term`` into a ``term_view``. The +``term_view`` type resembles an AST, but crucially it is not +recursive: its subterms have type ``term`` rather than ``term_view``. + +.. code-block:: + :caption: Part of the ``term_view`` type. + + noeq + type term_view = + | Tv_FVar : v:fv -> term_view + | Tv_App : hd:term -> a:argv -> term_view + | Tv_Abs : bv:binder -> body:term -> term_view + | Tv_Arrow : bv:binder -> c:comp -> term_view + ... + +The ``inspect`` primitves "peels away" one level of the abstraction +layer, giving access to the top-level shape of the term. + +The ``Tv_FVar`` node above represents (an ocurrence of) a global name. +The ``fv`` type is also abstract, and can be viewed as a ``name`` (which +is just ``list string``) via ``inspect_fv``. + +For instance, if we were to inspect ```qr_s`` (which we used above) +we would obtain a ``Tv_FVar v``, where ``inspect_fv v`` is something +like ``["Path"; "To"; "Module"; "qr_s"]``, that is, an "exploded" +representation of the fully-qualified name ``Path.To.Module.qr-s``. + +Every syntactic construct (terms, free variables, bound variables, +binders, computation types, etc) is modeled abstractly like ``term`` and +``fv``, and has a corresponding inspection functions. A list can be found +in ``FStar.Reflection.Builtins``. + +If the inspected term is an application, ``inspect`` will return a +``Tv_App f a`` node. Here ``f`` is a ``term``, so if we want to know its +structure we must recursively call ``inspect`` on it. The ``a`` part +is an *argument*, consisting of a ``term`` and an argument qualifier +(``aqualv``). The qualifier specifies if the application is implicit or +explicit. + +Of course, in the case of a nested application such as ``f x y``, this +is nested as ``(f x) y``, so inspecting it would return a ``Tv_App`` +node containing ``f x`` and ``y`` (with a ``Q_Explicit`` qualifier). +There are some helper functions defined to make inspecting applications +easier, like ``collect_app``, which decompose a term into its "head" and +all of the arguments the head is applied to. + + +Now, knowing this, we would then like a function to check if the goal is +a conjunction. +Naively, we need to inspect the goal to check that it is of the shape +``squash ((/\) a1 a2)``, that is, an application with two arguments +where the head is the symbol for a conjunction, i.e. ``(/\)``. This can +already be done with the ``term_view``, but is quite inconvenient due to +there being *too much* information in it. + +Meta-F* therefore provides another type, ``formula``, to represent logical +formulas more directly. Hence it suffices for us to call ``term_as_formula`` +and match on the result, like so: + +.. literalinclude:: ../code/Part5.IsConj.fst + :language: fstar + :start-after: //SNIPPET_START: isconj_2 + :end-before: //SNIPPET_END: isconj_2 + +The ``term_as_formula`` function, and all others that work on syntax, +are defined in "userspace" (that is, as library tactics/metaprograms) by +using ``inspect``. + +.. code-block:: + :caption: Part of the ``formula`` type. + + noeq + type formula = + | True_ : formula + | False_ : formula + | And : term -> term -> formula + | Or : term -> term -> formula + | Not : term -> formula + | Implies: term -> term -> formula + | Forall : bv -> term -> formula + ... + +.. note:: + + For experts: F* terms are (internally) represented with a + locally-nameless representation, meaning that variables do not have + a name under binders, but a de Bruijn index instead. While this has + many advantages, it is likely to be counterproductive when doing + tactics and metaprogramming, hence ``inspect`` *opens* variables when + it traverses a binder, transforming the term into a fully-named + representation. This is why ``inspect`` is effectul: it requires + freshness to avoid name clashes. If you prefer to work with a + locally-nameless representation, and avoid the effect label, you can + use ``inspect_ln`` instead (which will return ``Tv_BVar`` nodes instead + of ``Tv_Var`` ones). + +Dually, a ``term_view`` can be transformed into a ``term`` via the +``pack`` primitive, in order to build the syntax of any term. However, +it is usually more comfortable to use antiquotations (see above) for +building terms. + +Usual gotchas +------------- + +- The ``smt`` tactic does *not* immediately call the SMT solver. It merely + places the current goal into the "SMT Goal" list, all of which are sent + to the solver when the tactic invocation finishes. If any of these fail, + there is currently no way to "try again". + +- If a tactic is natively compiled and loaded as a plugin, editing its + source file may not have any effect (it depends on the build + system). You should recompile the tactic, just delete its object + file, or use the F* option ``--no_plugins`` to run it via the + interpreter temporarily, + +- When proving a lemma, we cannot just use ``_ by ...`` since the expected + type is just ``unit``. Workaround: assert the postcondition again, + or start without any binder. + +Coming soon +----------- + +- Metaprogramming +- Meta arguments and typeclasses +- Plugins (efficient tactics and metaprograms, ``--codegen Plugin`` and ``--load``) +- Tweaking the SMT options +- Automated coercions inspect/pack +- ``e <: C by ...`` +- Tactics can be used as steps of calc proofs. +- Solving implicits (Steel) + +.. + need pack, pack_ln + _ by + pre/post process + %splice + everparse, printers + --tactic_trace diff --git a/book/sec2/Design-of-fstar-Intro.rst.notes b/book/sec2/Design-of-fstar-Intro.rst.notes new file mode 100644 index 00000000000..3cdd557e31b --- /dev/null +++ b/book/sec2/Design-of-fstar-Intro.rst.notes @@ -0,0 +1,790 @@ +.. _Design-of-fstar-Intro: + +Elements of F* +============== + +The short tutorial should have provided you +with a basic feel for F*, in particular for how to use F* and its SMT +solving backend for programming and proving simple functional +programs. + +In this section, we step back and provide a more comprehensive +description of F*, starting from short summary of its design goals and +main technical features. Not all of these concepts may be familiar to +you at first, but by the end of this section, you should have gained a +working knowledge of the core design of F* as well as pointers to +further resources. + + +Core Language +------------- + +The syntax of F*'s core language is summarized by the simplified +grammar of terms, representing the core abstract syntax of terms used +by the F* typechecker. Note, this is not the full concrete syntax of +F* terms, which includes many additional features, including +parentheses for enforcing precedence, implicit arguments, n-ary +functions, various forms of syntactic sugar for common constructs like +tuples and sequencing, etc. However, the essence of F*'s core language +is distilled below. Understand how all these constructs fit together +and you should be able to see how the core of F* is simultaneously a +functional programming language and a logic. The goal of this section +is to explain all these constructs in detail, down to specifics of the +various syntactic conventions used in F*. + +Core abstract syntax of F* terms:: + + + Constants c ::= p primitive constant + | D user-defined data constructors + | T user-defined inductive type constructors + + Terms e, t ::= x variables + + | c constants and constructors + + + | fun (x:t) -> t' functions + + | t t' applications + + | match t with [b1 ... bn] pattern matching with zero or more cases + + | let x = t in t' let bindings + + | let rec f1 (x:t1) : t1' = e1 ... + and ... fn (x:tn) : tn' = en mutually recursive function definitions + + | x:t -> t function types (arrows) + + | x:t { t' } refinement types + + | Type u#U Type of types + + | x u#U1 ... u#Un Variable applied to one or more universes + + Case X ::= `|` P -> t Pattern-matching branch + + Pattern P ::= x Variable + | c Constant + | D [P1...Pn] Constructor applied to zero or more patterns + + Universe U ::= x Universe variable + | 0 Universe 0 + | U + 1 Successor universe + | max U U Maximum of universes + +Basic syntactic structure +......................... + +An F* program is a collection of :ref:`modules`, with each +module represented by a single file with the filename extension +``.fst``. Later, we'll see that a module's interface is in a separate +file. + +A module begins with the module's name and contains a sequence of +top-level signatures and definitions. + +* Signatures ascribe a type to a definition, e.g., ``val f : t``. + +Definitions come in several flavors: the two main forms we'll focus on +in this section are + +* possibly recursive definitions (let bindings, ``let [rec] f = e``) +* and, inductive type definitions (datatypes, ``type t = | D1 : t1 | ... | Dn : tn``) + +In later sections, we'll see two other kinds of definition: +user-defined indexed effects and sub-effects. + +Classes of Identifiers +^^^^^^^^^^^^^^^^^^^^^^ + +TODO: + +Comments +^^^^^^^^ + +Block comments are delimited by ``(*`` and ``*)``. Line comments begin +with ``//``. :: + + (* this is a + block comment *) + + + //This is a line comment + + +Primitive constants +................... + +Every F* program is checked in the context of some ambient primitive +definitions taken from the core F* module :ref:`Prims`. + +False +^^^^^ + +The type ``False`` has no elements. It represents a logical +falsehood in F*--- + + +Unit +^^^^ + +The type ``unit`` has a single element denoted ``()``. + + +Booleans +^^^^^^^^ + +The primitive type ``bool`` has two elements, ``true`` and +``false``. ``Prims`` also provides the following primitive boolean +operators + +* ``&&``: Boolean conjunction (infix) +* ``||``: Boolean disjunction (infix) +* ``not``: Boolean negation (prefix) + +TODO: Precedence + +Integers +^^^^^^^^ + +The type ``int`` represents unbounded, primitive mathematical +integers. Its elements are formed from the literals ``0, 1, 2, ...``, +and the following primitive operators: + +* ``-``: Unary negation (prefix) +* ``-``: Subtraction (infix) +* ``+``: Addition (infix) +* ``/``: Euclidean division (infix) +* ``%``: Euclidean modulus (infix) +* ``op_Multiply``: Unfortunately, the traditional multiplication symbol + ``*`` is reserved by default for the :ref:`tuple` type + constructor. Use the module ``FStar.Mul`` to treat ``*`` as integer + multiplication---see :ref:`this note`. +* ``<`` : Less than (infix) +* ``<=``: Less than or equal (infix) +* ``>`` : Greater than (infix) +* ``>=``: Greater than or equal (infix) + +TODO: Precedence + +Functions +......... + +F* provides several forms of syntactic sugar to define functions. The +syntax is largely inherited from OCaml, and this +`OCaml tutorial `_ +provides more details for those unfamiliar with the language. + +The following are synonyms:: + + let incr = fun (x:int) -> x + 1 + let incr (x:int) = x + 1 + +You can also let F* infer the type of the parameter ``x``:: + + let incr x = x + 1 + +Functions can take several arguments and the result type of a function +can also be annotated, if desired:: + + let incr (x:int) : int = x + 1 + let more_than_twice (x:int) (y:int) : bool = x > y + y + +It's considered good practice to annotate all the parameters and +result type of a top-level definition. + +.. note:: + + The type of any term in F* can be annotated using a *type + ascription*, ``e <: t``. This form instructs F* to check that the + term ``e`` has the type ``t``. For example, we could have written:: + + let incr = fun (x:int) -> (x + 1 <: int) + + We'll cover more about type ascriptions in this later + :ref:`section`. + + +User-defined operators and infix notation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Most commonly, to call, or "apply", a function, just place the +arguments to the right of the function. For example:: + + incr 0 // calls ``incr`` with the argument 0 + more_than_twice 17 8 //calls ``more_than_twice`` with ``17`` and ``8`` + +You can also immediately apply an unnamed function, or lambda term:: + + (fun (x:int) -> x + 1) 0 + +However, functions with two arguments can be applied in infix +notation, enclosing the function's name in "backticks". For example, +one could write, which can sometimes make code more readable. + + 17 \`more_than_twice\` 8 + +Functions can also be given names using special operator symbols, +e.g., one could write:: + + let (>>) = more_than_twice + +And then call the function using:: + + 17 >> 8 + +This `wiki page +`_ +provides more details on defining functions with operator symbols. + +Boolean refinement types +........................ + +Types are a way to describe collections of terms. For instance, the +type ``int`` describes terms which compute integer results, i.e., when +an ``int``-typed term is reduced fully it produces a value in the set +``{..., -2, -1, 0, 1, 2, ...}``. Similarly, the type ``bool`` is the type +of terms that compute or evaluate to one of the values in the set +``{true,false}``. + +One (naive but useful) mental model is to think of a type as +describing a set of values. With that in mind, and unlike in other +mainstream programming languages, one can contemplate defining types +for arbitrary sets of values. We will see a great many ways to define +such types, starting with boolean refinement types. + +Some simple refinement types +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:ref:`Prims` defines the type of natural numbers as:: + + let nat = x:int{x >= 0} + +This is an instance of a boolean refinement type, whose general form +is ``x:t { e }`` where ``t`` is a type, and ``e`` is a ``bool``-typed term +that may refer to the ``t``-typed bound variable ``x``. The term ``e`` +*refines* the type ``t``, in the sense that the set ``S`` denoted by ``t`` +is restricted to those elements ``x $\in$ S`` for which ``e`` evaluates to +``true``. + +That is the type ``nat`` describes the set of terms that evaluate to an +element of the set ``{0, 1, 2, 3, ...}``. + +But, there's nothing particularly special about ``nat``. You can define +arbitrary refinements of your choosing, e.g.,:: + + let empty = x:int { false } //one type for the empty set + let zero = x:int{ x = 0 } //the type containing one element `0` + let pos = x:int { x > 0 } //the positive numbers + let neg = x:int { x < 0 } //the negative numbers + let even = x:int { x % 2 = 0 } //the even numbers + let odd = x:int { x % 2 = 1 } //the odd numbers + +.. note:: + + Refinement types in F* trace their lineage to `F7 + `_, + a language developed at Microsoft Research c. 2007 -- 2011. `Liquid + Haskell `_ is + another language with refinement types. Those languages provide + additional background and resources for learning about refinement + types. + + Refinement types, in conjunction with dependent function types, + are, in principle, sufficient to encode many kinds of logics for + program correctness. However, refinement types are just one among + several tools in F* for program specification and proof. + +Refinement subtyping +^^^^^^^^^^^^^^^^^^^^ + +We have seen so far how to define a new refinement type, like ``nat`` or +``even``. However, to make use of refinement types we need rules that +allow us to: + +1. check that a program term has a given refinement type, e.g., to + check that ``0`` has type ``nat``. This is sometimes called + *introducing* a refinement type. + +2. make use of a term that has a refinement type, e.g., given ``x : + even`` we would like to be write ``x + 1``, treating ``x`` as an ``int`` + to add ``1`` to it. This is sometimes called *eliminating* a + refinement type. + +The technical mechanism in F* that supports both these features is +called *refinement subtyping*. + +If you're used to a language like Java, C# or some other +object-oriented language, you're familiar with the idea of +subtyping. A type ``t`` is a subtype of ``s`` whenever a program term of +type ``t`` can be safely treated as an ``s``. For example, in Java, all +object types are subtypes of the type ``Object``, the base class of all +objects. + +For boolean refinement types, the subtyping rules are as follows: + +* The type ``x:t { p }`` is a subtype of ``t``. That is, given ``e : + (x:t{p})``, it is always safe to *eliminate* the refinement and + consider ``e`` to also have type ``t``. + +* For a term ``e`` of type ``t`` (i.e., ``e : t``), ``t`` is a subtype of the + boolean refinement type ``x:t { p }`` whenever ``p[e / x]`` is provably + equal to ``true``. In other words, to *introduce* ``e : t`` at the + boolean refinement type ``x:t{ p }``, it suffices to prove that the + term ``p`` with ``e`` substituted for bound variable ``x``, evaluates to + ``true``. + +The the elimination rule for refinement types (i.e., the first part +above) is simple---with our intuition of types as sets, the refinement +type ``x:t{ p }`` *refines* the set corresponding to ``t`` by the +predicate ``p``, i.e., the ``x:t{ p }`` denotes a subset of ``t``, so, of +course ``x:t{ p }`` is a subtype of ``t``. + +The other direction is a bit more subtle: ``x:t{ p }`` is only a subtype +of ``p``, for those terms ``e`` that validate ``p``. You're probably also +wondering about how to prove that ``p[e/x]`` evaluates to ``true``---this +:ref:`part of the tutorial` should provide some +answers. But, the short version is that F*, by default, uses an SMT +solver to prove such fact, though you can also use tactics and other +techniques to do so. More information can be found +:ref:`here`. + +An example +++++++++++ + +Given ``x:even``, consider typechecking ``x + 1 : odd``; it takes a few +steps: + +1. The operator ``+`` expects both its arguments to have type ``int`` and + returns an ``int``. + +2. To prove that the first argument ``x:even`` is a valid argument for + ``+``, we use refinement subtyping to eliminate the refinement and + obtain ``x:int``. The second argument ``1:int`` already has the + required type. Thus, ``x + 1 : int``. + +3. To conclude that ``x + 1 : odd``, we need to introduce a refinement + type, by proving that the refinement predicate of ``odd`` evaluates + to true, i.e., ``x + 1 % 2 = 1``. This is provable by SMT, since we + started with the knowledge that ``x`` is even. + +As such, F* applies subtyping repeatedly to introduce and eliminate +refinement types, applying it multiple times even to check a simple +term like ``x + 1 : odd``. + + +Function types or arrows +........................ + +Functions are the main abstraction facility of any functional language +and their types are, correspondigly, the main specificational +construct. + +Total dependent functions +^^^^^^^^^^^^^^^^^^^^^^^^^ + +In its most basic form, function types have the shape:: + + x:t0 -> t1 + +This is the type of a function that + +1. receives an argument ``e`` of type ``t0``, and + +2. always returns a value of type ``t1[e / x]``, i.e., the type of the + returned value depends on the argument ``e``. + +It's worth emphasizing how this differs from function types in other +languages. + +* F*'s function type are dependent---the type of the result depends on + the argument. For example, we can write a function that returns a + ``bool`` when applied to an even number and returns a ``string`` when + applied to an odd number. + +* In F*'s core language, all functions are total, i.e., a function + call always terminates after consuming a finite but unbounded amount + of resources. + +.. note:: + + That said, on any given computer, it is possible for a function + call to fail to return due to resource exhaustion, e.g., running + out of memory. Later, as we look at :ref:`effects `, we + will see that F* also supports writing non-terminating functions. + +Some examples and common notation ++++++++++++++++++++++++++++++++++ + +1. Functions are *curried*. Functions that take multiple arguments are + written as functions that take the first argument and return a + function that takes the next argument and so on. For instance, the + type of integer addition is:: + + val (+) : x:int -> y:int -> int + +2. Not all functions are dependent and the name of the argument can be + omitted when it is not needed. For example, here's a more concise + way to write the type of ``(+)``:: + + val (+) : int -> int -> int + +3. Function types can be mixed with refinement types. For instance, + here's the type of integer division---the refinement on the divisor + forbids division-by-zero errors:: + + val (/) : int -> (divisor:int { divisor <> 0 }) -> int + +4. Dependence between the arguments and the result type can be used to + state relationships among them. For instance, there are several + types for the function ``let incr = (fun (x:int) -> x + 1)``:: + + val incr : int -> int + val incr : x:int -> y:int{y > x} + val incr : x:int -> y:int{y = x + 1} + + The first type ``(int -> int)`` is its traditional type in languages + like OCaml. + + The second type ``(x:int -> y:int{y > x})`` states that the returned + value ``y`` is greater than the argument ``x``. + + The third type is the most precise: ``(x:int -> y:int{y = x + 1})`` + states that the result ``y`` is exactly the increment of the argument + ``x``. + +5. It's often convenient to add refinements on arguments in a + dependent function type. For instance:: + + val f : x:(x:int{ x >= 1 }) -> y:(y:int{ y > x }) -> z:int{ z > x + y } + + Since this style is so common, and it is inconvenient to have to + bind two names for the parameters ``x`` and ``y``, F* allows (and + encourages) you to write:: + + val f : x:int{ x >= 1 } -> y:int{ y > x } -> z:int{ z > x + y } + +6. To emphasize that functions in F*'s core are total functions (i.e., + they always return a result), we sometimes annotate the result type + with the effect label "``Tot``". This label is optional, but + especially as we learn about :ref:`effects `, emphasizing + that some functions have no effects via the ``Tot`` label is + useful. For example, one might typically write:: + + val f : x:int{ x >= 1 } -> y:int{ y > x } -> Tot (z:int{ z > x + y }) + + adding a ``Tot`` annotation on the last arrow, to indicate that the + function has no side effects. One could also write:: + + val f : x:int{ x >= 1 } -> Tot (y:int{ y > x } -> Tot (z:int{ z > x + y })) + + adding an annotation on the intermediate arrow, though this is not + customary. + +Please refer to the section on :ref:`Implicit Arguments `, +where we explain the full syntax of binders, in function abstractions +and types. + +Type: The type of types +......................... + +One characteristic of F* (and many other dependently typed languages) +is that it treats programs and their types uniformly, all within a +single syntactic class. A type system in this style is sometimes +called a *Pure Type System* or `PTS +`_. + +In F* (as in other PTSs) types have types too, functions can take +types as arguments and return types as results, etc. In particular, +the type of a type is ``Type``, e.g., ``bool : Type``, ``int : Type``, ``int +-> int : Type`` etc. In fact, even ``Type`` has a type---as we'll see in +the subsection on :ref:`universes `. + +Parametric polymorphism or generics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Most modern typed languages provide a way to write programs with +generic types. For instance, C# and Java provide generics, C++ has +templates, and languages like OCaml and Haskell have several kinds of +polymorphic types. + +In F*, writing functions that are generic or polymorphic in types +arises naturally as a special case of dependent function types. For +example, here's a polymorphic identity function:: + + let id : a:Type -> a -> a = fun a x -> x + +There are a several things to note here: + +* The type of ``id`` is a dependent function type, with two + arguments. The first argument is ``a : Type``; the second argument is + a term of type ``a``; and the result also has the same type ``a``. + +* The definition of ``id`` is a lambda term with two arguments ``a : + Type`` (corresponding to the first argument type) and ``x : a``. The + function returns ``x``---it's an identity function on the second + argument. + +Here are some equivalent ways to write it:: + + let id = fun (a:Type) (x:a) -> x <: a + let id (a:Type) (x:a) : a = x + +To call ``id``, one can apply and check its type as shown:: + + id bool true : bool + id bool false : bool + id int (-1) : int + id nat 17 : nat + id string "hello" : string + id (int -> int) (fun x -> x) 0 : int + +.. note:: + + Exercises + + Try completing the following programs:: + + let apply : a:Type -> b:Type -> (a -> b) -> a -> b = + let compose : a:Type -> b:Type -> c:Type -> (b -> c) -> (a -> b) -> a -> c = + let twice : = fun a f x -> compose a a a f f x + +It's quite tedious to have to explicitly provide that first type +argument to ``id``. Implicit arguments and type inference will help, as +we'll see in :ref:`a later section `. + + +Type inference: Basics +...................... +.. _inference: + +Like many other languages in the tradition of +`Milner's ML `_, +type inference is a central component in F*'s design. + +You may be used to type inference in other languages, where one can +leave out type annotations (e.g., on variables, or when using +type-polymorphic (aka generic) functions) and the compiler determines +an appropriate type based on the surrounding program context. F*'s +type inference certainly includes such a feature, but is considerably +more powerful. Like in other dependently typed language, F*'s +inference engine is based on `higher-order unification +`_ +and can be used to infer arbitrary fragments of program text, not just +type annotations on variables. + +Let's consider our simple example of the definition and use of the +identity function again:: + + let id (a:Type) (x:a) : a = x + + id bool true : bool + id bool false : bool + id int (-1) : int + id nat 17 : nat + id string "hello" : string + id (int -> int) (fun x -> x) 0 : int + +Instead of explicitly providing that first type argument when applying +``id``, one could write it as follows, replacing the type arguments with +an underscore ``_``:: + + id _ true : bool + id _ false : bool + id _ (-1) : int + id _ 17 : nat + id _ "hello" : string + id _ (fun x -> x) 0 : int + +The underscore symbols is a wildcard, or a hole in program, and it's +the job of the F* typechecker to fill in the hole. + +.. note:: + + Program holes are a very powerful concept and form the basis of + Meta-F*, the metaprogramming and tactics framework embedded in + F*---we'll see more about holes in a :ref:`later + section`. + + +Implicit arguments +^^^^^^^^^^^^^^^^^^ +.. _implicits: + +Since it's tedious to write an ``_`` everywhere, F* has a notion of +*implicit arguments*. That is, when defining a function, one can add +annotations to indicate that certain arguments can be omitted at call +sites and left for the typechecker to infer automatically. + +For example, one could write:: + + let id (#a:Type) (x:a) : a = x + +decorating the first argument ``a`` with a ``#``, to indicate that it is +an implicit argument. Then at call sites, one can simply write:: + + id true + id 0 + id (fun x -> x) 0 + +And F* will figure out instantiations for the missing first argument +to ``id``. + +In some cases, it may be useful to actually provide an implicit +argument explicitly, rather than relying on the F* to pick one. For +example, one could write the following:: + + id #nat 0 + id #(x:int{x == 0}) 0 + id #(x:int{x <> 1}) 0 + +In each case, we provide the first argument of ``id`` explicitly, by +preceding it with a ``#`` sign, which instructs F* to take the user's +term rather than generating a hole and trying to fill it. + +Universes +......... + +.. _universes: + +As mentioned before, every well-typed term in F* has a type, and this +is true of the type ``Type`` itself. In some languages that are +designed only for programming rather than both programs and proofs, +the type of ``Type`` is itself ``Type``, a kind of circularity known +as `impredicativity +`_. This circularity +leads to paradoxes and can make a logic inconsistent. + +As such, F*, like many other dependently typed systems, employ a +system of *universes*. The type ``Type`` actually comes in (countably) +infinite variants, written ``Type u#0``, ``Type u#1``, ``Type u#2``, +etc. The ``u#i`` annotation following the ``Type`` is called a +*universe level*, where ``Type u#i`` has type ``Type u#(i + 1)``. One +way to think of it is the each universe level contains an entire copy +of ``F*``'s type system, with higher universes being large enough to +accommodate copies of the systems available at all lower levels. + +This may seem a bit mind-bending at first. And, indeed, the universe +system of F* can often be ignored, since F* will infer universes +levels, e.g., one can just write ``Type`` instead of picking a +specific universe level. That said, occasionally, the universe +constraints will make themselves known and preventy you from doing +certain things that can break consistency. Nevertheless, universes are +a crucial feature that allow F* programs to abstract over nearly all +elements of the language (e.g., one can write functions from types to +types, or store types within data structures) while remaining +logically consistent. + +F*'s type system is universe polymorphic, meaning that by default, a defin + + + + + + +Syntax of binders +................. + +Having informally introduced implicit arguments, we can now present a +first take at the syntax of binders in F*. + +**Binding occurrences**: A binding occurence `b` of a variable +introduces a variable in a scope and is associated with one of several +language constructs, including a lambda abstraction, a refinement +type, a let binding, etc. Each binding occurrence is in one of several +forms: + + 1. The form ``x:t``, declaring a variable ``x`` at type ``t`` + + 2. The ``#x:t``, indicating that the binding is for an implicit + argument ``x`` of type ``t``. + +In many cases the type annotation on a binder can be omitted, + +Later, we will see additional forms of binding occurrences, including +versions that associate attributes with binders and others with +various forms of type-inference hints. + +**Introducing binders**: The syntax ``fun (b1) ... (bn) -> t`` +introduces a lambda abstraction, whereas ``b1 -> .. bn -> t`` is the +shape of a function type. + + +Decidable equality and `eqtype` +............................... + + + +Let bindings +............ + + +Inductive type definitions +.......................... + +.. _tuples: + +Discriminators +^^^^^^^^^^^^^^ + +Projectors +^^^^^^^^^^ + +Equality +^^^^^^^^ + +Positivity +^^^^^^^^^^ + +Universe constraints +^^^^^^^^^^^^^^^^^^^^ + +Pattern matching +................ + + +Recursive definitions and termination +..................................... + + +Refinement Types +................ + + +Proof irrelevance, squash types and classical logic +................................................... + + +Misc +.... + + +Evaluation strategy +^^^^^^^^^^^^^^^^^^^ + +.. _ascriptions: + +Effects +------- +.. _effects: + + +Modules and Interfaces +---------------------- +.. _modules: + +.. toctree:: + :hidden: + :maxdepth: 1 + :caption: Contents: + +A Mental Model of the F* Typechecker +------------------------------------ +.. _mental-model:refinements: + + +Dangling + +.. _tutorial:refinements: diff --git a/book/smt2_pygments.py b/book/smt2_pygments.py new file mode 100644 index 00000000000..e11e8fb2426 --- /dev/null +++ b/book/smt2_pygments.py @@ -0,0 +1,36 @@ +from pygments.lexer import RegexLexer, words +from pygments.token import * + +# very rough lexer; not 100% precise +class CustomLexer(RegexLexer): + name = 'SMT2' + aliases = ['smt2'] + filenames = ['*.smt2'] + keywords = ( + 'assert' , + 'declare-datatypes', + 'declare-fun' , + 'declare-sort', + 'define-fun' , + 'set-option' , + 'pattern' , + 'weight' , + 'qid' , + 'check-sat' , + 'named' , + ) + tokens = { + 'root': [ + (r' ', Text), + (r'\n', Text), + (r'\r', Text), + (r';.*\n', Comment), + (words(keywords, suffix=r'\b'), Keyword), + (r'0x[0-9a-fA-F_]+', Literal.Number), + (r'[0-9_]+', Literal.Number), + (r'[a-zA-Z_]+', Text), + (r'.', Text), + ] + } + +#class CustomFormatter: diff --git a/book/static/custom.css b/book/static/custom.css new file mode 100644 index 00000000000..7292bc09251 --- /dev/null +++ b/book/static/custom.css @@ -0,0 +1,12 @@ +.toggle .header { + display: block; + clear: both; +} + +.toggle .header:after { + content: "Reveal ▶"; +} + +.toggle .header.open:after { + content: "Hide ▼"; +} diff --git a/book/structure.rst b/book/structure.rst new file mode 100644 index 00000000000..d0c5f63bc1d --- /dev/null +++ b/book/structure.rst @@ -0,0 +1,201 @@ + + + +Structure of this book +====================== + +**This book is a work in progress** + +The first four parts of this book explain the main features of the +language using a variety of examples. You should read them +sequentially, following along with the associated code samples and +exercises. These first four parts are arranged in increasing order of +complexity---you can stop after any of them and have a working +knowledge of useful fragments of F*. + +The remaining parts of the book are more loosely connected and either +provide a reference guide to the compiler and libraries, or develop +case studies that the reader can choose depending on their +interest. Of course, some of those case studies come with +prerequisites, e.g., you must have read about effects before tackling +the case study on parsers and formatters. + +* Part 1: Basic Functional Programming and Proofs + + +The first part of this book provides a basic introduction to +programming with pure total functions, refinement types, and SMT-based +proofs, and how to compile and execute your first F* program. This +part of the book revises a previous online tutorial on F* and is +targeted at an audience familiar with programming, though with no +background in formal proofs. Even if you are familiar with program +proofs and dependent types, it will be useful to quickly go through +this part, since some elements are quite specific to F*. + +* Part 2: Inductive Types for Data, Proofs, and Computations + +We turn next to inductive type definitions, the main mechanism by +which a user can define new data types. F*'s indexed inductive types +allow one to capture useful properties of data structures, and +dependently types functions over these indexed types can be proven to +respect several kinds of invariants. Beyond their use for data +structures, inductive data types are used at the core of F*'s logic to +model fundamental notions like equality and termination proofs, and +can also be used to model and embed other programming paradigms within +F*. + + +.. + Part 2: Dependently Typed Functional Programming + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + .. _Universes: + .. _TypeConversion: + + * Working with indexed data structures + - Vectors + - Red-black trees + - Merkle trees + + * Equality, type conversion, and subtyping + + .. _Classical: + + * Proof irrelevance and classical logic: prop and squash + + * More termination proofs + - Infinitely branching trees and ordinal numbers + - Lexicographic orderings and unification + + * Calculational Proofs + + * Generic programming + - Printf + - Integer overloading + - Codes for types + + * Typeclasses + + * Universes + +* Part 3: Modularity with Interfaces and Typeclasses + + +We discuss two main abstraction techniques, useful in structuring +larger developments: interfaces and typeclasses. Interfaces are a +simple information hiding mechanism built in to F*'s module +system. Typeclasses are suitable for more advanced developments, +providing more flexible abstraction patterns coupled with +custom type inference. + +* Part 4: Computational Effects + +We introduce F*'s effect system, starting with its primitive effects +for total, ghost, and divergent computations. We also provide a brief +primer on Floyd-Hoare logic and weakest precondition calculi, +connecting them to Dijkstra monads, a core concept in the design of +F*'s effect system. + +* Part 5: Tactics and Metaprogramming + +We introduce Meta-F*, the metaprogramming system included in +F*. Meta-F* can be used to automate the construction of proofs as well +as programmatically construct fragments of F* programs. There's a lot +to cover here---the material so far presents the basics of how to get +started with using Meta-F* to target specific assertions in your +program and to have their proofs be solved using a mixture of tactics +and SMT solving. + +* Under the hood: F* & SMT + +In this part of the book, we cover how F* uses the Z3 SMT solver. We +present a brief overview of F*'s SMT encoding paying attention in +particular to F* use of fuel to throttle SMT solver's unfolding of +recursive functions and inductive type definitions. We also cover a +bit of how quantifier instantiation works, how to profile Z3's +quantifier instantiation, and some strategies for how to control +proofs that are too slow because of excessive quantifier +instantiation. + + +.. _effects: + +* Planned content + +The rest of the book is still in the works, but the planned content is +the following: + + + Part 4: User-defined Effects + + - State + + - Exceptions + + - Concurrency + + - Algebraic Effects + + + + Part 5: Tactics and Metaprogramming + + - Reflecting on syntax + + - Holes and proof states + + - Builtin tactics + + - Derived tactics + + - Interactive proofs + + - Custom decision procedures + + - Proofs by reflection + + - Synthesizing programs + + - Tactics for program extraction + + + + Part 6: F* Libraries + + + + Part 7: A User's Guide to Structuring and Maintaining F* Developments + + - The Build System + -- Dependence Analysis + -- Checked files + -- Sample project + + - Using the F* editor + + - Proofs by normalization + * Normalization steps + * Call-by-name vs. call-by-value + * Native execution and plugins + + - Proof Engineering + * Building, maintaining and debugging stable proofs + + - Extraction + * OCaml + * F# + * KaRaMeL + * Partial evaluation + + - Command line options + + - A guide to various F* error messages + + - Syntax guide + + - FAQ + + + Part 8: Steel: A Concurrent Separation Logic Embedded in F* + + + Part 9: Application to High-assurance Cryptography + + + Part 10: Application to Parsers and Formatters + + + diff --git a/book/under_the_hood/under_the_hood.rst b/book/under_the_hood/under_the_hood.rst new file mode 100644 index 00000000000..8cebff6d102 --- /dev/null +++ b/book/under_the_hood/under_the_hood.rst @@ -0,0 +1,17 @@ +.. _Under_the_hood: + +############## +Under the hood +############## + +In this part of the book, we'll look at some of the inner workings of +F*, things that you will eventually need to know to become an expert +user of the system. We'll cover F*'s SMT encoding, its two +normalization engines, its plugin framework, and other topics. + + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + uth_smt diff --git a/book/under_the_hood/uth_smt.rst b/book/under_the_hood/uth_smt.rst new file mode 100644 index 00000000000..0e39a6ad17c --- /dev/null +++ b/book/under_the_hood/uth_smt.rst @@ -0,0 +1,2020 @@ +.. _UTH_smt: + +Understanding how F* uses Z3 +============================ + +As we have seen throughout, F* relies heavily on the Z3 SMT +(Satifiability Modulo Theories) solver for proof automation. Often, on +standalone examples at the scale covered in earlier chapters, the +automation just works out of the box, but as one builds larger +developments, proof automation can start becoming slower or +unpredictable---at that stage, it becomes important to understand how +F*'s encoding to SMT works to better control proofs. + +At the most abstract level, one should realize that finding proofs in +the SMT logic F* uses (first-order logic with uninterpreted functions +and arithmetic) is an undecidable problem. As such, F* and the SMT +solver relies on various heuristics and partial decision procedures, +and a solver like Z3 does a remarkable job of being able to +effectively solve the very large problems that F* presents to it, +despite the theoretical undecidability. That said, the proof search +that Z3 uses is computationally expensive and can be quite sensitive +to the choice of heuristics and syntactic details of problem +instances. As such, if one doesn't chose the heuristics well, a small +change in a query presented to Z3 can cause it to take a different +search path, perhaps causing a proof to not be found at all, or to be +found after consuming a very different amount of resources. + +Some background and resources: + + * F*'s SMT encoding uses the `SMT-LIB v2 + `_ language. We refer + to the "SMT-LIB v2" language as SMT2. + + * Alejandro Aguirre wrote a `tech report + `_ + describing work in progress towards formalizing F*'s SMT encoding. + + * Michal Moskal's `Programming with Triggers + `_ describes how to pick + triggers for quantifier instantiation and how to debug and profile + the SMT solver, in the context of Vcc and the relation Hypervisor + Verification project. + + * Leonardo de Moura and Nikolaj Bjorner `describe how E-matching is + implemented in Z3 + `_ (at least + circa 2007). + +A Primer on SMT2 +---------------- + +SMT2 is a standardized input language supported by many SMT +solvers. Its syntax is based on `S-expressions +`_, inspired by languages +in the LISP family. We review some basic elements of its syntax here, +particularly the parts that are used by F*'s SMT encoding. + +* Multi-sorted logic + + The logic provided by the SMT solver is multi-sorted: the sorts + provide a simple type system for the logic, ensuring, e.g., that + terms from two different sorts can never be equal. A user can define + a new sort ``T``, as shown below: + + .. code-block:: smt2 + + (declare-sort T) + + Every sort comes with a built-in notion of equality. Given two terms + ``p`` and ``q`` of the same sort ``T``, ``(= p q)`` is a term of + sort ``Bool`` expressing their equality. + + +* Declaring uninterpreted functions + + A new function symbol ``F``, with arguments in sorts + ``sort_1 .. sort_n`` and returning a result in ``sort`` is declared + as shown below, + + .. code-block:: smt2 + + (declare-fun F (sort_1 ... sort_n) sort) + + The function symbol ``F`` is *uninterpreted*, meaning that the only + information the solver has about ``F`` is that it is a function, + i.e., when applied to equal arguments ``F`` produces equal results. + +* Theory symbols + + Z3 provides support for several *theories*, notably integer and + real arithmetic. For example, on terms ``i`` and ``j`` of ``Int`` + sort, the sort of unbounded integers, the following terms define + the expected arithmetic functions: + + .. code-block:: smt2 + + (+ i j) ; addition + (- i j) ; subtraction + (* i j) ; multiplication + (div i j) ; Euclidean division + (mod i j) ; Euclidean modulus + + +* Logical connectives + + SMT2 provides basic logical connectives as shown below, where ``p`` + and ``q`` are terms of sort ``Bool`` + + .. code-block:: smt2 + + (and p q) ; conjunction + (or p q) ; disjunction + (not p) ; negation + (implies p q) ; implication + (iff p q) ; bi-implication + + + SMT2 also provides support for quantifiers, where the terms below + represent a term ``p`` with the variables ``x1 ... xn`` universally + and existentially quantified, respectively. + + + .. code-block:: smt2 + + (forall ((x1 sort_1) ... (xn sort_n)) p) + (exists ((x1 sort_1) ... (xn sort_n)) p) + +* Attribute annotations + + A term ``p`` can be decorated with attributes names ``a_1 .. a_n`` + with values ``v_1 .. v_n`` using the following syntax---the ``!`` is + NOT to be confused with logical negation. + + .. code-block:: smt2 + + (! p + :a_1 v_1 + ... + :a_n v_n) + + A common usage is with quantifiers, as we'll see below, e.g., + + .. code-block:: smt2 + + (forall ((x Int)) + (! (implies (>= x 0) (f x)) + :qid some_identifier)) + +* An SMT2 theory and check-sat + + An SMT2 theory is a collection of sort and function symbol + declarations, and assertions of facts about them. For example, + here's a simple theory declaring a function symbol ``f`` and an + assumption that ``f x y`` is equivalent to ``(>= x y)``---note, + unlike in F*, the ``assert`` keyword in SMT2 assumes that a fact is + true, rather than checking that it is valid, i.e., ``assert`` in + SMT2 is like ``assume`` in F*. + + + .. code-block:: smt2 + + (declare-fun f (Int Int) Bool) + + (assert (forall ((x Int) (y Int)) + (iff (>= y x) (f x y)))) + + + In the context of this theory, one can ask whether some facts about + ``f`` are valid. For example, to check if ``f`` is a transitive + function, one asserts the *negation* of the transitivity + property for ``f`` and then asks Z3 to check (using the + ``(check-sat)`` directive) if the resulting theory is satisfiable. + + .. code-block:: smt2 + + (assert (not (forall ((x Int) (y Int) (z Int)) + (implies (and (f x y) (f y z)) + (f x z))))) + (check-sat) + + In this case, Z3 very quickly responds with ``unsat``, meaning that + there are no models for the theory that contain an interpretation of + ``f`` compatible with both assertions, or, equivalently, the + transitivity of ``f`` is true in all models. That is, we expect + successful queries to return ``unsat``. + + +A Brief Tour of F*'s SMT Encoding +--------------------------------- + +Consider the following simple F* code: + +.. code-block:: fstar + + let id x = x + let f (x:int) = + if x < 0 + then assert (- (id x) >= 0) + else assert (id x >= 0) + +To encode the proof obligation of this program to SMT, F* generates an +SMT2 file with the following rough shape. + +.. code-block:: smt2 + + ;; Some basic scaffoling + + (declare-sort Term) + ... + + ;; Encoding of some basic modules + + (declare-fun Prims.bool () Term) + ... + + ;; Encoding of background facts about the current module + + (declare-fun id (Term) Term) + (assert (forall ((x Term)) (= (id x) x))) + + ;; Encoding the query, i.e., negated proof obligation + + (assert (not (forall ((x Term)) + (and (implies (lt x 0) (geq (minus (M.id x)) 0)) + (implies (not (lt x 0)) (geq (M.id x) 0)))))) + + (check-sat) + + ;; Followed by some instrumentation for error reporting + ;; in case the check-sat call fails (i.e., does not return unsat) + +That was just just to give you a rough idea---the details of F*'s +actual SMT encoding are a bit different, as we'll see below. + +To inspect F*'s SMT encoding, we'll work through several small +examples and get F* to log the SMT2 theories that it generates. For +this, we'll use the file shown below as a skeleton, starting with the +``#push-options "--log_queries"`` directive, which instructs F* to +print out its encoding to ``.smt2`` file. The ``force_a_query`` +definition at the end ensures that F* actually produces at least one +query---without it, F* sends nothing to the Z3 and so prints no output +in the .smt2 file. If you run ``fstar.exe SMTEncoding.fst`` on the +command line, you will find a file ``queries-SMTEncoding.smt2`` in the +current directory. + +.. literalinclude:: ../code/SMTEncoding.fst + :language: fstar + +Even for a tiny module like this, you'll see that the .smt2 file is +very large. That's because, by default, F* always includes the modules +``prims.fst``, ``FStar.Pervasives.Native.fst``, and +``FStar.Pervasives.fsti`` as dependences of other modules. Encoding +these modules consumes about 150,000 lines of SMT2 definitions and +comments. + +The encoding of each module is delimited in the .smt2 file by comments +of the following kind: + +.. code-block:: smt2 + + ;;; Start module Prims + ... + ;;; End module Prims (1334 decls; total size 431263) + + ;;; Start module FStar.Pervasives.Native + ... + ;;; End module FStar.Pervasives.Native (2643 decls; total size 2546449) + + ;;; Start interface FStar.Pervasives + ... + ;;; End interface FStar.Pervasives (2421 decls; total size 1123058) + +where each `End` line also describes the number of declarations in +the module and its length in characters. + + +``Term`` sort +............. + +Despite SMT2 being a multi-sorted logic, aside from the pervasive use +the SMT sort ``Bool``, F*'s encoding to SMT (mostly) uses just a +single sort: every pure (or ghost) F* term is encoded to the SMT +solver as an instance of an uninterpreted SMT sort called +``Term``. This allows the encoding to be very general, handling F*'s +much richer type system, rather than trying to map F*'s complex type +system into the much simpler type system of SMT sorts. + + +Booleans +........ + +One of the most primitive sorts in the SMT solver is ``Bool``, the +sort of Booleans. All the logical connectives in SMT are operations on +the ``Bool`` sort. To encode values of the F* type ``bool`` to SMT, we +use the ``Bool`` sort, but since all F* terms are encoded to the +``Term`` sort, we "box" the ``Bool`` sort to promote it to ``Term``, +using the SMT2 definitions below. + +.. code-block:: smt2 + + (declare-fun BoxBool (Bool) Term) + (declare-fun BoxBool_proj_0 (Term) Bool) + (assert (! (forall ((@u0 Bool)) + (! (= (BoxBool_proj_0 (BoxBool @u0)) + @u0) + :pattern ((BoxBool @u0)) + :qid projection_inverse_BoxBool_proj_0)) + :named projection_inverse_BoxBool_proj_0)) + +This declares two uninterpreted functions ``BoxBool`` and +``BoxBool_proj_0`` that go back and forth between the sorts ``Bool`` +and ``Term``. + +The axiom named ``projection_inverse_BoxBool_proj_0`` states that +``BoxBool_proj_0`` is the inverse of ``BoxBool``, or, equivalently, +that ``BoxBool`` is an injective function from ``Bool`` to +``Term``. + + +The ``qid`` is the quantifier identifier, usually equal to or derived +from the name of the assumption that includes it---qids come up when +we look at :ref:`profiling quantifier instantiation `. + +Patterns for quantifier instantiation +..................................... + +The ``projection_inverse_BoxBool_proj_0`` axiom on booleans shows our +first use of a quantified formula with a pattern, i.e., the part that +says ``:pattern ((BoxBool @u0))``. These patterns are the main +heuristic used to control the SMT solver's proof search and will +feature repeatedly in the remainder of this chapter. + +When exploring a theory, the SMT solver has a current partial model +which contains an assignment for some of the variables in a theory to +ground terms. All the terms that appear in this partial model are +called `active` terms and these active terms play a role in quantifier +instantiation. + +Each universally quantified formula in scope is a term of the form below: + +.. code-block:: smt2 + + (forall ((x1 s1) ... (xn sn)) + (! ( body ) + :pattern ((p1) ... (pm)))) + +This quantified formula is inert and only plays a role in the solver's +search once the bound variables ``x1 ... xn`` are instantiated. The +terms ``p1 ... pm`` are called patterns, and collectively, ``p1 +... pm`` must mention *all* the bound variables. To instantiate the +quantifier, the solver aims to find active terms ``v1...vm`` that +match the patterns ``p1..pm``, where a match involves finding a +substitution ``S`` for the bound variables ``x1...xm``, such that the +substituted patterns ``S(p1...pm)`` are equal to the active terms +``v1..vm``. Given such a substitution, the substituted term +``S(body)`` becomes active and may refine the partial model further. + +Existentially quantified formulas are dual to universally quantified +formulas. Whereas a universal formula in the *context* (i.e., in +negative position, or as a hypothesis) is inert until its pattern is +instantiated, an existential *goal* (or, in positive position) is +inert until its pattern is instantiated. Existential quantifiers can +be decorated with patterns that trigger instantiation when matched +with active terms, just like universal quantifiers + +Returning to ``projection_inverse_BoxBool_proj_0``, what this means is +that once the solver has an active term ``BoxBool b``, it can +instantiate the quantified formula to obtain ``(= (BoxBool_proj_0 +(BoxBool b)) b)``. + +Integers +........ + +The encoding of the F* type ``int`` is similar to that of +``bool``---the primitive SMT sort ``Int`` (of unbounded mathematical +integers) are coerced to ``Term`` using the injective function +``BoxInt``. + +.. code-block:: smt2 + + (declare-fun BoxInt (Int) Term) + (declare-fun BoxInt_proj_0 (Term) Int) + (assert (! (forall ((@u0 Int)) + (! (= (BoxInt_proj_0 (BoxInt @u0)) + @u0) + :pattern ((BoxInt @u0)) + :qid projection_inverse_BoxInt_proj_0)) + :named projection_inverse_BoxInt_proj_0)) + +The primitive operations on integers are encoded by unboxing the +arguments and boxing the result. For example, here's the encoding of +``Prims.(+)``, the addition operator on integers. + +.. code-block:: smt2 + + (declare-fun Prims.op_Addition (Term Term) Term) + (assert (! (forall ((@x0 Term) (@x1 Term)) + (! (= (Prims.op_Addition @x0 + @x1) + (BoxInt (+ (BoxInt_proj_0 @x0) + (BoxInt_proj_0 @x1)))) + :pattern ((Prims.op_Addition @x0 + @x1)) + :qid primitive_Prims.op_Addition)) + :named primitive_Prims.op_Addition)) + +This declares an uninterpreted function ``Prims.op_Addition``, a +binary function on ``Term``, and an assumption relating it to the SMT +primitive operator from the integer arithmetic theory ``(+)``. The +pattern allows the SMT solver to instantiate this quantifier for every +active application of the ``Prims.op_Addition``. + +The additional boxing introduces some overhead, e.g., proving ``x + y +== y + x`` in F* amounts to proving ``Prims.op_Addition x y == +Prims.op_Addition y x`` in SMT2. This in turn involves instantiation +quantifiers, then reasoning in the theory of linear arithmetic, and +finally using the injectivity of the ``BoxInt`` function to +conclude. However, this overhead is usually not perceptible, and the +uniformity of encoding everything to a single ``Term`` sort simplifies +many other things. Nevertheless, F* provides a few options to control +the way integers and boxed and unboxed, described :ref:`ahead +`. + + +Functions +......... + +Consider the F* function below: + +.. code-block:: fstar + + let add3 (x y z:int) : int = x + y + z + + +Its encoding to SMT has several elements. + +First, we have have a declaration of an uninterpreted ternary function +on ``Term``. + +.. code-block:: smt2 + + (declare-fun SMTEncoding.add3 (Term Term Term) Term) + +The semantics of ``add3`` is given using the assumption below, which +because of the pattern on the quantifier, can be interpreted as a +rewrite rule from left to right: every time the solver has +``SMTEncoding.add3 x y z`` as an active term, it can expand it to its +definition. + +.. code-block:: smt2 + + (assert (! (forall ((@x0 Term) (@x1 Term) (@x2 Term)) + (! (= (SMTEncoding.add3 @x0 + @x1 + @x2) + (Prims.op_Addition (Prims.op_Addition @x0 + @x1) + @x2)) + :pattern ((SMTEncoding.add3 @x0 + @x1 + @x2)) + :qid equation_SMTEncoding.add3)) + + :named equation_SMTEncoding.add3)) + +In addition to its definition, F* encodes *the type of* ``add3`` to the +solver too, as seen by the assumption below. One of the key predicates +of F*'s SMT encoding is ``HasType``, which relates a term to its +type. The assumption ``typing_SMTEncoding.add3`` encodes the typing of +the application based on the typing hypotheses on the arguments. + +.. code-block:: smt2 + + (assert (! (forall ((@x0 Term) (@x1 Term) (@x2 Term)) + (! (implies (and (HasType @x0 + Prims.int) + (HasType @x1 + Prims.int) + (HasType @x2 + Prims.int)) + (HasType (SMTEncoding.add3 @x0 @x1 @x2) + Prims.int)) + :pattern ((SMTEncoding.add3 @x0 @x1 @x2)) + :qid typing_SMTEncoding.add3)) + :named typing_SMTEncoding.add3)) + +This is all we'd need to encode ``add3`` if it was never used at +higher order. However, F* treats functions values just like any other +value and allows them to be passed as arguments to, or returned as +results from, other functions. The SMT logic is, however, a +first-order logic and functions like ``add3`` are not first-class +values. So, F* introduces another layer in the encoding to model +higher-order functions, but we don't cover this here. + + +.. _UTH_SMT_fuel: + +Recursive functions and fuel +............................ + +Non-recursive functions are similar to macro definitions---F* just +encodes encodes their semantics to the SMT solver as a rewrite +rule. However, recursive functions, since they could be unfolded +indefinitely, are not so simple. Let's look at the encoding of the +``factorial`` function shown below. + +.. code-block:: fstar + + open FStar.Mul + let rec factorial (n:nat) : nat = + if n = 0 then 1 + else n * factorial (n - 1) + + + +First, we have, as before, an uninterpreted function symbol on ``Term`` +and an assumption about its typing. + +.. code-block:: smt2 + + (declare-fun SMTEncoding.factorial (Term) Term) + + (assert (! (forall ((@x0 Term)) + (! (implies (HasType @x0 Prims.nat) + (HasType (SMTEncoding.factorial @x0) Prims.nat)) + :pattern ((SMTEncoding.factorial @x0)) + :qid typing_SMTEncoding.factorial)) + :named typing_SMTEncoding.factorial)) + + +However, to define the semantics of ``factorial`` we introduce a +second "fuel-instrumented" function symbol with an additional +parameter of ``Fuel`` sort. + +.. code-block:: smt2 + + (declare-fun SMTEncoding.factorial.fuel_instrumented (Fuel Term) Term) + +The ``Fuel`` sort is declared at the very beginning of F*'s SMT +encoding and is a representation of unary integers, with two +constructors ``ZFuel`` (for zero) and ``SFuel f`` (for successor). + +The main idea is to encode the definition of ``factorial`` guarded by +patterns that only allow unfolding the definition if the fuel argument +of ``factorial.fuel_instrumented`` is not zero, as shown below. +Further, the assumption defining the semantics of +``factorial.fuel_instrumented`` is guarded by a typing hypothesis on +the argument ``(HasType @x1 Prims.nat)``, since the recursive function +in F* is only well-founded on ``nat``, not on all terms. The +``:weight`` annotation is an SMT2 detail: setting it to zero ensures +that the SMT solver can instantiate this quantifier as often as +needed, so long as the the fuel instrumentation argument is non-zero. +Notice that the equation peels off one application of ``SFuel``, so +that the quantifier cannot be repeatedly instantiated infinitely. + +.. code-block:: smt2 + + (assert (! (forall ((@u0 Fuel) (@x1 Term)) + (! (implies (HasType @x1 Prims.nat) + (= (SMTEncoding.factorial.fuel_instrumented (SFuel @u0) @x1) + (let ((@lb2 (Prims.op_Equality Prims.int @x1 (BoxInt 0)))) + (ite (= @lb2 (BoxBool true)) + (BoxInt 1) + (Prims.op_Multiply + @x1 + (SMTEncoding.factorial.fuel_instrumented + @u0 + (Prims.op_Subtraction @x1 (BoxInt 1)))))))) + :weight 0 + :pattern ((SMTEncoding.factorial.fuel_instrumented (SFuel @u0) @x1)) + :qid equation_with_fuel_SMTEncoding.factorial.fuel_instrumented)) + :named equation_with_fuel_SMTEncoding.factorial.fuel_instrumented)) + +We also need an assumption that tells the SMT solver that the fuel +argument, aside from controlling the number of unfoldings, is +semantically irrelevant. + +.. code-block:: smt2 + + (assert (! (forall ((@u0 Fuel) (@x1 Term)) + (! (= (SMTEncoding.factorial.fuel_instrumented (SFuel @u0) @x1) + (SMTEncoding.factorial.fuel_instrumented ZFuel @x1)) + :pattern ((SMTEncoding.factorial.fuel_instrumented (SFuel @u0) @x1)) + :qid @fuel_irrelevance_SMTEncoding.factorial.fuel_instrumented)) + :named @fuel_irrelevance_SMTEncoding.factorial.fuel_instrumented)) + +And, finally, we relate the original function to its fuel-instrumented +counterpart. + +.. code-block:: smt2 + + (assert (! (forall ((@x0 Term)) + (! (= (SMTEncoding.factorial @x0) + (SMTEncoding.factorial.fuel_instrumented MaxFuel @x0)) + :pattern ((SMTEncoding.factorial @x0)) + :qid @fuel_correspondence_SMTEncoding.factorial.fuel_instrumented)) + :named @fuel_correspondence_SMTEncoding.factorial.fuel_instrumented)) + +This definition uses the constant ``MaxFuel``. The value of this +constant is determined by the F* options ``--initial_fuel n`` and +``--max_fuel m``. When F* issues a query to Z3, it tries the query +repeatedly with different values of ``MaxFuel`` ranging between ``n`` +and ``m``. Additionally, the option ``--fuel n`` sets both the initial +fuel and max fuel to ``n``. + +This single value of ``MaxFuel`` controls the number of unfoldings of +`all` recursive functions in scope. Of course, the patterns are +arranged so that if you have a query involving, say, ``List.map``, +quantified assumptions about an unrelated recursive function like +``factorial`` should never trigger. Neverthless, large values of +``MaxFuel`` greatly increase the search space for the SMT solver. If +your proof requires a setting greater than ``--fuel 2``, and if it +takes the SMT solver a long time to find the proof, then you should +think about whether things could be done differently. + +However, with a low value of ``fuel``, the SMT solver cannot reason +about recursive functions beyond that bound. For instance, the +following fails, since the solver can unroll the definition only once +to conclude that ``factorial 1 == 1 * factorial 0``, but being unable +to unfold ``factorial 0`` further, the proof fails. + +.. code-block:: fstar + + #push-options "--fuel 1" + let _ = assert (factorial 1 == 1) (* fails *) + +As with regular functions, the rest of the encoding of recursive +functions has to do with handling higher-order uses. + +Inductive datatypes and ifuel +............................. + +Inductive datatypes in F* allow defining unbounded structures and, +just like with recursive functions, F* encodes them to SMT by +instrumenting them with fuel, to prevent infinite unfoldings. Let's +look at a very simple example, an F* type of unary natural numbers. + +.. code-block:: fstar + + type unat = + | Z : unat + | S : (prec:unat) -> unat + +Although Z3 offers support for a built-in theory of datatypes, F* does +not use it (aside for ``Fuel``), since F* datatypes are more +complex. Instead, F* rolls its own datatype encoding using +uninterpreted functions and the encoding of ``unat`` begins by +declaring these functions. + +.. code-block:: smt2 + + (declare-fun SMTEncoding.unat () Term) + (declare-fun SMTEncoding.Z () Term) + (declare-fun SMTEncoding.S (Term) Term) + (declare-fun SMTEncoding.S_prec (Term) Term) + +We have one function for the type ``unat``; one for each constructor +(``Z`` and ``S``); and one "projector" for each argument of each +constructor (here, only ``S_prec``, corresponding to the F* projector +``S?.prec``). + +The type ``unat`` has its typing assumption, where ``Tm_type`` is the +SMT encoding of the F* type ``Type``---note F* does not encode the +universe levels to SMT. + +.. code-block:: smt2 + + (assert (! (HasType SMTEncoding.unat Tm_type) + :named kinding_SMTEncoding.unat@tok)) + +The constructor ``S_prec`` is assumed to be an inverse of ``S``. If +there were more than one argument to the constructor, each projector +would project out only the corresponding argument, encoding that the +constructor is injective on each of its arguments. + +.. code-block:: smt2 + + (assert (! (forall ((@x0 Term)) + (! (= (SMTEncoding.S_prec (SMTEncoding.S @x0)) @x0) + :pattern ((SMTEncoding.S @x0)) + :qid projection_inverse_SMTEncoding.S_prec)) + :named projection_inverse_SMTEncoding.S_prec)) + +The encoding defines two macros ``is-SMTEncoding.Z`` and +``is-SMTEncoding.S`` that define when the head-constructor of a term +is ``Z`` and ``S`` respectively. These two macros are used in the +definition of the inversion assumption of datatypes, namely that given +a term of type ``unat``, one can conclude that its head constructor +must be either ``Z`` or ``S``. However, since the type ``unat`` is +unbounded, we want to avoid applying this inversion indefinitely, so +it uses a quantifier with a pattern that requires non-zero fuel to +be triggered. + +.. code-block:: smt2 + + (assert (! (forall ((@u0 Fuel) (@x1 Term)) + (! (implies (HasTypeFuel (SFuel @u0) @x1 SMTEncoding.unat) + (or (is-SMTEncoding.Z @x1) + (is-SMTEncoding.S @x1))) + :pattern ((HasTypeFuel (SFuel @u0) @x1 SMTEncoding.unat)) + :qid fuel_guarded_inversion_SMTEncoding.unat)) + :named fuel_guarded_inversion_SMTEncoding.unat)) + +Here, we see a use of ``HasTypeFuel``, a fuel-instrumented version of +the ``HasType`` we've seen earlier. In fact, ``(HasType x t)`` is just +a macro for ``(HasTypeFuel MaxIFuel x t)``, where much like for +recursive functions and fuel, the constant ``MaxIFuel`` is defined by +the current value of the F* options ``--initial_ifuel``, +``--max_ifuel``, and ``--ifuel`` (where ``ifuel`` stands for "inversion fuel"). + +The key bit in ensuring that the inversion assumption above is not +indefinitely applied is in the structure of the typing assumptions for +the data constructors. These typing assumptions come in two forms, +introduction and elimination. + +The introduction form for the ``S`` constructor is shown below. This +allows deriving that ``S x`` has type ``unat`` from the fact that +``x`` itself has type ``unat``. The pattern on the quantifier makes +this goal-directed: if ``(HasTypeFuel @u0 (SMTEncoding.S @x1) +SMTEncoding.unat)`` is already an active term, then the quantifer +fires to make ``(HasTypeFuel @u0 @x1 SMTEncoding.unat)`` an active +term, peeling off one application of the ``S`` constructor. If we +were to use ``(HasTypeFuel @u0 @x1 SMTEncoding.unat)`` as the pattern, +this would lead to an infinite quantifier instantiation loop, since +every each instantiation would lead a new, larger active term that +could instantiate the quantifier again. Note, using the introduction +form does not vary the fuel parameter, since the the number of +applications of the constructor ``S`` decreases at each instantiation +anyway. + +.. code-block:: smt2 + + (assert (! (forall ((@u0 Fuel) (@x1 Term)) + (! (implies (HasTypeFuel @u0 @x1 SMTEncoding.unat) + (HasTypeFuel @u0 (SMTEncoding.S @x1) SMTEncoding.unat)) + :pattern ((HasTypeFuel @u0 (SMTEncoding.S @x1) SMTEncoding.unat)) + :qid data_typing_intro_SMTEncoding.S@tok)) + :named data_typing_intro_SMTEncoding.S@tok)) + +The elimination form allows concluding that the sub-terms of a +well-typed application of a constructor are well-typed too. This time +note that the conclusion of the rule decreases the fuel parameter by +one. If that were not the case, then we would get a quantifier +matching loop between ``data_elim_SMTEncoding.S`` and +``fuel_guarded_inversion_SMTEncoding.unat``, since each application of +the latter would contribute an active term of the form ``(HasTypeFuel +(SFuel _) (S (S_prec x)) unat)``, allowing the former to be triggered +again. + +.. code-block:: smt2 + + (assert (! (forall ((@u0 Fuel) (@x1 Term)) + (! (implies (HasTypeFuel (SFuel @u0) (SMTEncoding.S @x1) SMTEncoding.unat) + (HasTypeFuel @u0 @x1 SMTEncoding.unat)) + :pattern ((HasTypeFuel (SFuel @u0) (SMTEncoding.S @x1) SMTEncoding.unat)) + :qid data_elim_SMTEncoding.S)) + :named data_elim_SMTEncoding.S)) + +A final important element in the encoding of datatypes has to do with +the well-founded ordering used in termination proofs. The following +states that if ``S x1`` is well-typed (with non-zero fuel) then ``x1`` +precedes ``S x1`` in F*'s built-in sub-term ordering. + +.. code-block:: smt2 + + (assert (! (forall ((@u0 Fuel) (@x1 Term)) + (! (implies (HasTypeFuel (SFuel @u0) + (SMTEncoding.S @x1) + SMTEncoding.unat) + (Valid (Prims.precedes Prims.lex_t Prims.lex_t + @x1 (SMTEncoding.S @x1)))) + :pattern ((HasTypeFuel (SFuel @u0) (SMTEncoding.S @x1) SMTEncoding.unat)) + :qid subterm_ordering_SMTEncoding.S)) + :named subterm_ordering_SMTEncoding.S)) + +Once again, a lot of the rest of the datatype encoding has to do with +handling higher order uses of the constructors. + +As with recursive functions, the single value of ``MaxIFuel`` controls +the number of inversions of all datatypes in scope. It's a good idea +to try to use an ``ifuel`` setting that is as low as possible for +your proofs, e.g., a value less than ``2``, or even ``0``, if +possible. However, as with fuel, a value of ifuel that is too low will +cause the solver to be unable to prove some facts. For example, +without any ``ifuel``, the solver cannot use the inversion assumption +to prove that the head of ``x`` must be either ``S`` or ``Z``, and F* +reports the error "Patterns are incomplete". + +.. code-block:: fstar + + #push-options "--ifuel 0" + let rec as_nat (x:unat) : nat = + match x with (* fails exhaustiveness check *) + | S x -> 1 + as_nat x (* fails termination check *) + | Z -> 0 + +Sometimes it is useful to let the solver arbitrarily invert an +inductive type. The ``FStar.Pervasives.allow_inversion`` is a library +function that enables this, as shown below. Within that scope, the +ifuel guards on the ``unat`` type are no longer imposed and SMT can +invert ``unat`` freely---F* accepts the code below. + +.. code-block:: fstar + + #push-options "--ifuel 0" + let rec as_nat (x:unat) : nat = + allow_inversion unat; + match x with + | S x -> 1 + as_nat x + | Z -> 0 + +This can be useful sometimes, e.g., one could set the ifuel to 0 and +allow inversion within a scope for only a few selected types, e.g., +``option``. However, it is rarely a good idea to use +``allow_inversion`` on an unbounded type (e.g., ``list`` or even +``unat``). + + +Logical Connectives +.................... + +The :ref:`logical connectives ` that F* offers are all +derived forms. Given the encodings of datatypes and functions (and +arrow types, which we haven't shown), the encodings of all these +connectives just fall out naturally. However, all these connectives +also have built-in support in the SMT solver as part of its +propositional core and support for E-matching-based quantifier +instantiation. So, rather than leave them as derived forms, a vital +optimization in F*'s SMT encoding is to recognize these connectives +and to encode them directly to the corresponding forms in SMT. + +The term ``p /\ q`` in F* is encoded to ``(and [[p]] [[q]]])`` where +``[[p]]`` and ``[[q]]`` are the *logical* encodings of ``p`` and ``q`` +respectively. However, the SMT connective ``and`` is a binary function +on the SMT sort ``Bool``, whereas all we have been describing so far +is that every F* term ``p`` is encoded to the SMT sort ``Term``. To +bridge the gap, the logical encoding of a term ``p`` interprets the +``Term`` sort into ``Bool`` by using a function ``Valid p``, which +deems a ``p : Term`` to be valid if it is inhabited, as per the +definitions below. + +.. code-block:: smt2 + + (declare-fun Valid (Term) Bool) + (assert (forall ((e Term) (t Term)) + (! (implies (HasType e t) (Valid t)) + :pattern ((HasType e t) (Valid t)) + :qid __prelude_valid_intro))) + +The connectives ``p \/ q``, ``p ==> q``, ``p <==> q``, and ``~p`` are +similar. + +The quantified forms ``forall`` and ``exists`` are mapped to the +corresponding quantifiers in SMT. For example, + +.. code-block:: fstar + + let fact_positive = forall (x:nat). factorial x >= 1 + +is encoded to: + +.. code-block:: smt2 + + (forall ((@x1 Term)) + (implies (HasType @x1 Prims.nat) + (>= (BoxInt_proj_0 (SMTEncoding.factorial @x1)) + (BoxInt_proj_0 (BoxInt 1))))) + +Note, this quantifier does not have any explicitly annotated +patterns. In this case, Z3's syntactic trigger selection heuristics +pick a pattern: it is usually the smallest collection of sub-terms of +the body of the quantifier that collectively mention all the bound +variables. In this case, the choices for the pattern are +``(SMTEncoding.factorial @x1)`` and ``(HasType @x1 Prims.nat)``: Z3 +picks both of these as patterns, allowing the quantifier to be +triggered if an active term matches either one of them. + +For small developments, leaving the choice of pattern to Z3 is often +fine, but as your project scales up, you probably want to be more +careful about your choice of patterns. F* lets you write the pattern +explicitly on a quantifier and translates it down to SMT, as shown +below. + +.. code-block:: fstar + + let fact_positive_2 = forall (x:nat).{:pattern (factorial x)} factorial x >= 1 + +This produces: + +.. code-block:: smt2 + + (forall ((@x1 Term)) + (! (implies (HasType @x1 Prims.nat) + (>= (BoxInt_proj_0 (SMTEncoding.factorial @x1)) + (BoxInt_proj_0 (BoxInt 1)))) + :pattern ((SMTEncoding.factorial.fuel_instrumented ZFuel @x1)))) + + +Note, since ``factorial`` is fuel instrumented, the pattern is +translated to an application that requires no fuel, so that the +property also applies to any partial unrolling of factorial also. + +Existential formulas are similar. For example, one can write: + +.. code-block:: fstar + + let fact_unbounded = forall (n:nat). exists (x:nat). factorial x >= n + +And it gets translated to: + +.. code-block:: smt2 + + (forall ((@x1 Term)) + (implies (HasType @x1 Prims.nat) + (exists ((@x2 Term)) + (and (HasType @x2 Prims.nat) + (>= (BoxInt_proj_0 (SMTEncoding.factorial @x2)) + (BoxInt_proj_0 @x1)))))) + +.. _z3_and_smtencoding_options: + +Options for Z3 and the SMT Encoding +................................... + +F* provides two ways of passing options to Z3. + +The option ``--z3cliopt `` causes F* to pass the string as a +command-line option when starting the Z3 process. A typical usage +might be ``--z3cliopt 'smt.random_seed=17'``. + +In contrast, ``--z3smtopt `` causes F* to send the string to +Z3 as part of its SMT2 output and this option is also reflected in the +.smt2 file that F* emits with ``--log_queries``. As such, it can be +more convenient to use this option if you want to debug or profile a +run of Z3 on an .smt2 file generated by F*. A typical usage would be +``--z3smtopt '(set-option :smt.random_seed 17)'``. Note, it is +possible to abuse this option, e.g., one could use ``--z3smtopt +'(assert false)'`` and all SMT queries would trivially pass. So, use +it with care. + +F*'s SMT encoding also offers a few options. + +* ``--smtencoding.l_arith_repr native`` + +This option requests F* to inline the definitions of the linear +arithmetic operators (``+`` and ``-``). For example, with this option +enabled, F* encodes the term ``x + 1 + 2`` as the SMT2 term below. + +.. code-block:: smt2 + + (BoxInt (+ (BoxInt_proj_0 (BoxInt (+ (BoxInt_proj_0 @x0) + (BoxInt_proj_0 (BoxInt 1))))) + (BoxInt_proj_0 (BoxInt 2)))) + +* ``--smtencoding.elim_box true`` + +This option is often useful in combination with +``smtencoding.l_arith_repr native``, enables an optimization to remove +redundant adjacent box/unbox pairs. So, adding this option to the +example above, the encoding of ``x + 1 + 2`` becomes: + +.. code-block:: smt2 + + (BoxInt (+ (+ (BoxInt_proj_0 @x0) 1) 2)) + + +* ``--smtencoding.nl_arith_repr [native|wrapped|boxwrap]`` + +This option controls the representation of non-linear arithmetic +functions (``*, /, mod``) in the SMT encoding. The default is +``boxwrap`` which uses the encoding of ``Prims.op_Multiply, +Prims.op_Division, Prims.op_Modulus`` analogous to +``Prims.op_Addition``. + +The ``native`` setting is similar to the ``smtencoding.l_arith_repr +native``. When used in conjuction with ``smtencoding.elim_box true``, +the F* term ``x * 1 * 2`` is encoded to: + +.. code-block:: smt2 + + (BoxInt (* (* (BoxInt_proj_0 @x0) 1) 2)) + +However, a third setting ``wrapped`` is also available with provides +an intermediate level of wrapping. With this setting enabled, the +encoding of ``x * 1 * 2`` becomes + +.. code-block:: smt2 + + (BoxInt (_mul (_mul (BoxInt_proj_0 @x0) 1) 2)) + +where ``_mul`` is declared as shown below: + +.. code-block:: smt2 + + (declare-fun _mul (Int Int) Int) + (assert (forall ((x Int) (y Int)) (! (= (_mul x y) (* x y)) :pattern ((_mul x y))))) + +Now, you may wonder why all these settings are useful. Surely, one +would think, ``--smtencoding.l_arith_repr native +--smtencoding.nl_arith_repr native --smtencoding.elim_box true`` is +the best setting. However, it turns out that the additional layers of +wrapping and boxing actually enable some proofs to go through, and, +empirically, no setting strictly dominates all the others. + +However, the following is a good rule of thumb if you are starting a +new project: + +1. Consider using ``--z3smtopt '(set-option :smt.arith.nl + false)'``. This entirely disables support for non-linear arithmetic + theory reasoning in the SMT solver, since this can be very + expensive and unpredictable. Instead, if you need to reason about + non-linear arithmetic, consider using the lemmas from + ``FStar.Math.Lemmas`` to do the non-linear steps in your proof + manually. This will be more painstaking, but will lead to more + stable proofs. + +2. For linear arithmetic, the setting ``--smtencoding.l_arith_repr + native --smtencoding.elim_box true`` is a good one to consider, and + may yield some proof performance boosts over the default setting. + +.. _UTH_smt_patterns: + +Designing a Library with SMT Patterns +------------------------------------- + +In this section, we look at the design of ``FStar.Set``, a module in +the standard library, examining, in particular, its use of SMT +patterns on lemmas for proof automation. The style used here is +representative of the style used in many proof-oriented +libraries---the interface of the module offers an abstract type, with +some constructors and some destructors, and lemmas that relate their +behavior. + +To start with, for our interface, we set the fuel and ifuel both to +zero---we will not need to reason about recursive functions or invert +inductive types here. + +.. literalinclude:: ../code/SimplifiedFStarSet.fsti + :language: fstar + :start-after: //SNIPPET_START: module$ + :end-before: //SNIPPET_END: module$ + +Next, we introduce the signature of the main abstract type of this +module, ``set``: + +.. literalinclude:: ../code/SimplifiedFStarSet.fsti + :language: fstar + :start-after: //SNIPPET_START: set$ + :end-before: //SNIPPET_END: set$ + +Sets offer just a single operation called ``mem`` that allows testing +whether or not a given element is in the set. + +.. literalinclude:: ../code/SimplifiedFStarSet.fsti + :language: fstar + :start-after: //SNIPPET_START: destructor$ + :end-before: //SNIPPET_END: destructor$ + +However, there are several ways to construct sets: + +.. literalinclude:: ../code/SimplifiedFStarSet.fsti + :language: fstar + :start-after: //SNIPPET_START: constructors$ + :end-before: //SNIPPET_END: constructors$ + +Finally, sets are equipped with a custom equivalence relation: + +.. literalinclude:: ../code/SimplifiedFStarSet.fsti + :language: fstar + :start-after: //SNIPPET_START: equal$ + :end-before: //SNIPPET_END: equal$ + +The rest of our module offers lemmas that describe the behavior of +``mem`` when applied to each of the constructors. + +.. literalinclude:: ../code/SimplifiedFStarSet.fsti + :language: fstar + :start-after: //SNIPPET_START: core_properties$ + :end-before: //SNIPPET_END: core_properties$ + +Each of these lemmas should be intuitive and familiar. The extra bit +to pay attention to is the ``SMTPat`` annotations on each of the +lemmas. These annotations instruct F*'s SMT encoding to treat the +lemma like a universal quantifier guarded by the user-provided +pattern. For instance, the lemma ``mem_empty`` is encoded to the SMT +solver as shown below. + +.. code-block:: smt2 + + (assert (! (forall ((@x0 Term) (@x1 Term)) + (! (implies (and (HasType @x0 Prims.eqtype) + (HasType @x1 @x0)) + (not (BoxBool_proj_0 + (SimplifiedFStarSet.mem @x0 + @x1 + (SimplifiedFStarSet.empty @x0))))) + :pattern ((SimplifiedFStarSet.mem @x0 + @x1 + (SimplifiedFStarSet.empty @x0))) + :qid lemma_SimplifiedFStarSet.mem_empty)) + :named lemma_SimplifiedFStarSet.mem_empty)) + +That is, from the perspective of the SMT encoding, the statement of +the lemma ``mem_empty`` is analogous to the following assumption: + +.. code-block:: fstar + + forall (a:eqtype) (x:a). {:pattern (mem x empty)} not (mem x empty) + + +As such, lemmas decorated with SMT patterns allow the user to inject +new, quantified hypotheses into the solver's context, where each of +those hypotheses is justified by a proof in F* of the corresponding +lemma. This allows users of the ``FStar.Set`` library to treat ``set`` +almost like a new built-in type, with proof automation to reason about +its operations. However, making this work well requires some careful +design of the patterns. + +Consider ``mem_union``: the pattern chosen above allows the solver to +decompose an active term ``mem x (union s1 s2)`` into ``mem x s1`` and +``mem x s2``, where both terms are smaller than the term we started +with. Suppose instead that we had written: + +.. code-block:: fstar + + val mem_union (#a:eqtype) (x:a) (s1 s2:set a) + : Lemma + (ensures (mem x (union s1 s2) == (mem x s1 || mem x s2))) + [SMTPat (mem x s1); SMTPat (mem x s2)] + +This translates to an SMT quantifier whose patterns are the pair of +terms ``mem x s1`` and ``mem x s2``. This choice of pattern would +allow the solver to instantiate the quantifier with all pairs of +active terms of the form ``mem x s``, creating more active terms that +are themselves matching candidates. To be explicit, with a single +active term ``mem x s``, the solver would derive ``mem x (union s +s)``, ``mem x (union s (union s s))``, and so on. This is called a +matching loop and can be disastrous for solver performance. So, +carefully chosing the patterns on quantifiers and lemmas with +``SMTPat`` annotations is important. + +Finally, to complete our interface, we provide two lemmas to +characterize ``equal``, the equivalence relation on sets. The first +says that sets that agree on the ``mem`` function are ``equal``, and +the second says that ``equal`` sets are provably equal ``(==)``, and +the patterns allow the solver to convert reasoning about equality into +membership and provable equality. + +.. literalinclude:: ../code/SimplifiedFStarSet.fsti + :language: fstar + :start-after: //SNIPPET_START: equal_intro_elim$ + :end-before: //SNIPPET_END: equal_intro_elim$ + +Of course, all these lemmas can be easily proven by F* under a +suitable representation of the abstract type ``set``, as shown in the +module implementation below. + +.. literalinclude:: ../code/SimplifiedFStarSet.fst + :language: fstar + :start-after: //SNIPPET_START: SimplifiedFStarSet.Impl$ + :end-before: //SNIPPET_END: SimplifiedFStarSet.Impl$ + +Exercise +........ + +Extend the set library with another constructor with the signature +shown below: + +.. code-block:: fstar + + val from_fun (#a:eqtype) (f: a -> bool) : Tot (set a) + +and prove a lemma that shows that a an element ``x`` is in ``from_fun +f`` if and only if ``f x = true``, decorating the lemma with the +appropriate SMT pattern. + +This `interface file <../code/SimplifiedFStarSet.fsti>`_ and its +`implementation <../code/SimplifiedFStarSet.fst>`_ provides the +definitions you need. + +.. container:: toggle + + .. container:: header + + **Answer** + + Look at `FStar.Set.intension `_ if you get stuck + +-------------------------------------------------------------------------------- + +.. _Profiling_z3: + +Profiling Z3 and Solving Proof Performance Issues +------------------------------------------------- + +At some point, you will write F* programs where proofs start to take +much longer than you'd like: simple proofs fail to go through, or +proofs that were once working start to fail as you make small changes +to your program. Hopefully, you notice this early in your project and +can try to figure out how to make it better before slogging through +slow and unpredictable proofs. Contrary to the wisdom one often +receives in software engineering where early optimization is +discouraged, when developing proof-oriented libraries, it's wise to +pay attention to proof performance issues as soon as they come up, +otherwise you'll find that as you scale up further, proofs become so +slow or brittle that your productivity decreases rapidly. + +Query Statistics +................ + +Your first tool to start diagnosing solver performance is F*'s +``--query_stats`` option. We'll start with some very simple artificial +examples. + +With the options below, F* outputs the following statistics: + + +.. code-block:: fstar + + #push-options "--initial_fuel 0 --max_fuel 4 --ifuel 0 --query_stats" + let _ = assert (factorial 3 == 6) + + +.. code-block:: none + + ((20,0-20,49)) Query-stats (SMTEncoding._test_query_stats, 1) failed + {reason-unknown=unknown because (incomplete quantifiers)} in 31 milliseconds + with fuel 0 and ifuel 0 and rlimit 2723280 + statistics={mk-bool-var=7065 del-clause=242 num-checks=3 conflicts=5 + binary-propagations=42 arith-fixed-eqs=4 arith-pseudo-nonlinear=1 + propagations=10287 arith-assert-upper=21 arith-assert-lower=18 + decisions=11 datatype-occurs-check=2 rlimit-count=2084689 + arith-offset-eqs=2 quant-instantiations=208 mk-clause=3786 + minimized-lits=3 memory=21.41 arith-pivots=6 max-generation=5 + arith-conflicts=3 time=0.03 num-allocs=132027456 datatype-accessor-ax=3 + max-memory=21.68 final-checks=2 arith-eq-adapter=15 added-eqs=711} + + ((20,0-20,49)) Query-stats (SMTEncoding._test_query_stats, 1) failed + {reason-unknown=unknown because (incomplete quantifiers)} in 47 milliseconds + with fuel 2 and ifuel 0 and rlimit 2723280 + statistics={mk-bool-var=7354 del-clause=350 arith-max-min=10 interface-eqs=3 + num-checks=4 conflicts=8 binary-propagations=56 arith-fixed-eqs=17 + arith-pseudo-nonlinear=3 arith-bound-prop=2 propagations=13767 + arith-assert-upper=46 arith-assert-lower=40 decisions=25 + datatype-occurs-check=5 rlimit-count=2107946 arith-offset-eqs=6 + quant-instantiations=326 mk-clause=4005 minimized-lits=4 + memory=21.51 arith-pivots=20 max-generation=5 arith-add-rows=34 + arith-conflicts=4 time=0.05 num-allocs=143036410 datatype-accessor-ax=5 + max-memory=21.78 final-checks=6 arith-eq-adapter=31 added-eqs=1053} + + ((20,0-20,49)) Query-stats (SMTEncoding._test_query_stats, 1) succeeded + in 48 milliseconds with fuel 4 and ifuel 0 and rlimit 2723280 + statistics={arith-max-min=26 num-checks=5 binary-propagations=70 arith-fixed-eqs=47 + arith-assert-upper=78 arith-assert-lower=71 decisions=40 + rlimit-count=2130332 max-generation=5 arith-nonlinear-bounds=2 + time=0.05 max-memory=21.78 arith-eq-adapter=53 added-eqs=1517 + mk-bool-var=7805 del-clause=805 interface-eqs=3 conflicts=16 + arith-pseudo-nonlinear=6 arith-bound-prop=4 propagations=17271 + datatype-occurs-check=5 arith-offset-eqs=20 quant-instantiations=481 + mk-clause=4286 minimized-lits=38 memory=21.23 arith-pivots=65 + arith-add-rows=114 arith-conflicts=5 num-allocs=149004462 + datatype-accessor-ax=9 final-checks=7} + +There's a lot of information here: + +* We see three lines of output, each tagged with a source location and + an internal query identifer (``(SMTEncoding._test_query_stats, 1)``, + the first query for verifying ``_test_query_stats``). + +* The first two attempts at the query failed, with Z3 reporting the + reason for failure as ``unknown because (incomplete + quantifiers)``. This is a common response from Z3 when it fails to + prove a query---since first-order logic is undecidable, when Z3 + fails to find a proof, it reports "unknown" rather than claiming + that the theory is satisfiable. The third attempt succeeded. + +* The attempts used ``0``, ``2``, and ``4`` units of fuel. Notice that + our query was ``factorial 3 == 6`` and this clearly requires at + least 4 units of fuel to succeed. In this case it didn't matter + much, since the two failed attempts took only ``47`` and ``48`` + milliseconds. But, you may sometimes find that there are many + attempts of a proof with low fuel settings and finally success with + a higher fuel number. In such cases, you may try to find ways to + rewrite your proof so that you are not relying on so many unrollings + (if possible), or if you decide that you really need that much fuel, + then setting the ``--fuel`` option to that value can help avoid + several slow failures and retries. + +* The rest of the statistics report internal Z3 statistics. + + - The ``rlimit`` value is a logical resource limit that F* sets when + calling Z3. Sometimes, as we will see shortly, a proof can be + "cancelled" in case Z3 runs past this resource limit. You can + increase the ``rlimit`` in this case, as we'll see below. + + - Of the remaning statistics, perhaps the main one of interest is + ``quant_instantiations``. This records a cumulative total of + quantifiers instantiated by Z3 so far in the current + session---here, each attempt seems to instantiate around 100--150 + quantifiers. This is a very low number, since the query is so + simple. You may be wondering why it is even as many as that, since + 4 unfolding of factorial suffice, but remember that there are many + other quantifiers involved in the encoding, e.g., those that prove + that ``BoxBool`` is injective etc. A more typical query will see + quantifier instantiations in the few thousands. + + +.. note:: + + Note, since the ``quant-instantiations`` metric is cumulative, it + is often useful to precede a query with something like the following: + + .. code-block:: fstar + + #push-options "--initial_fuel 0 --max_fuel 4 --ifuel 0 --query_stats" + #restart-solver + let _dummy = assert (factorial 0 == 1) + + let _test_query_stats = assert (factorial 3 == 6) + + The ``#restart-solver`` creates a fresh Z3 process and the + ``dummy`` query "warms up" the process by feeding it a trivial + query, which will run somewhat slow because of various + initialization costs in the solver. Then, the query stats reported + for the real test subject starts in this fresh session. + +Working though a slow proof +........................... + +Even a single poorly chosen quantified assumption in the prover's +context can make an otherwise simple proof take very long. To +illustrate, consider the following variation on our example above: + +.. code-block:: fstar + + assume Factorial_unbounded: forall (x:nat). exists (y:nat). factorial y > x + + #push-options "--fuel 4 --ifuel 0 --query_stats" + #restart-solver + let _test_query_stats = assert (factorial 3 == 6) + +We've now introduced the assumption ``Factorial_unbounded`` into our +context. Recall from the SMT encoding of quantified formulas, from the +SMT solver's perspective, this looks like the following: + +.. code-block:: smt2 + + (assert (! (forall ((@x0 Term)) + (! (implies (HasType @x0 Prims.nat) + (exists ((@x1 Term)) + (! (and (HasType @x1 Prims.nat) + (> (BoxInt_proj_0 (SMTEncoding.factorial @x1)) + (BoxInt_proj_0 @x0))) + :qid assumption_SMTEncoding.Factorial_unbounded.1))) + :qid assumption_SMTEncoding.Factorial_unbounded)) + :named assumption_SMTEncoding.Factorial_unbounded)) + + +This quantifier has no explicit patterns, but Z3 picks the term +``(HasType @x0 Prims.nat)`` as the pattern for the ``forall`` +quantifier. This means that it can instantiate the quantifier for +active terms of type ``nat``. But, a single instantiation of the +quantifier, yields the existentially quantified formula. Existentials +are immediately `skolemized +`_ by Z3, i.e., the +existentially bound variable is replaced by a fresh function symbol +that depends on all the variables in scope. So, a fresh term ``a @x0`` +corresponding ``@x1`` is introduced, and immediately, the conjunct +``HasType (a @x0) Prims.nat`` becomes an active term and can be used to +instantiate the outer universal quantifier again. This "matching loop" +sends the solver into a long, fruitless search and the simple proof +about ``factorial 3 == 6`` which previously succeeded in a few +milliseconds, now fails. Here's are the query stats: + + +.. code-block:: none + + ((18,0-18,49)) Query-stats (SMTEncoding._test_query_stats, 1) failed + {reason-unknown=unknown because canceled} in 5647 milliseconds + with fuel 4 and ifuel 0 and rlimit 2723280 + statistics={ ... quant-instantiations=57046 ... } + +A few things to notice: + + * The failure reason is "unknown because canceled". That means the + solver reached its resource limit and halted the proof + search. Usually, when you see "canceled" as the reason, you could + try raising the rlimit, as we'll see shortly. + + * The failure took 5.6 seconds. + + * There were 57k quantifier instantiations, as compared to just the + 100 or so we had earlier. We'll soon seen how to pinpoint which + quantifiers were instantiated too much. + +Increasing the rlimit +~~~~~~~~~~~~~~~~~~~~~ + +We can first retry the proof by giving Z3 more resources---the +directive below doubles the resource limit given to Z3. + +.. code-block:: fstar + + #push-options "--z3rlimit_factor 2" + +This time it took 14 seconds and failed. But if you try the same proof +a second time, it succeeds. That's not very satisfying. + +Repeating Proofs with Quake +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although this is an artificial example, unstable proofs that work and +then suddenly fail do happen. Z3 does guarantee that it is +deterministic in a very strict sense, but even the smallest change to +the input, e.g., a change in variable names, or even asking the same +query twice in a succession in the same Z3 session, can result in +different answers. + +There is often a deeper root cause (in our case, it's the +``Factorial_unbounded`` assumption, of course), but a first attempt at +determining whether or not a proof is "flaky" is to use the F* option +``--quake``. + +.. code-block:: fstar + + #push-options "--quake 5/k" + let _test_query_stats = assert (factorial 3 == 6) + +This tries the query 5 times and reports the number of successes and +failures. + +In this case, F* reports the following: + +.. code-block:: none + + Quake: query (SMTEncoding._test_query_stats, 1) succeeded 4/5 times (best fuel=4, best ifuel=0) + +If you're working to stabilize a proof, a good criterion is to see if +you can get the proof to go through with the ``--quake`` option. + +You can also try the proof by varying the Z3's random seed and +checking that it works with several choices of the seed. + +.. code-block:: none + + #push-options "--z3smtopt '(set-option :smt.random_seed 1)'" + + +Profiling Quantifier Instantiation +.................................. + +We have a query that's taking much longer than we'd like and from the +query-stats we see that there are a lot of quantifier instances. Now, +let's see how to pin down which quantifier is to blame. + + + 1. Get F* to log an .smt2 file, by adding the ``--log_queries`` + option. It's important to also add a ``#restart-solver`` before + just before the definition that you're interested in + profiling. + + .. code-block:: fstar + + #push-options "--fuel 4 --ifuel 0 --query_stats --log_queries --z3rlimit_factor 2" + #restart-solver + let _test_query_stats = assert (factorial 3 == 6) + + F* reports the name of the file that it wrote as part of the + query-stats. For example: + + .. code-block:: none + + ((18,0-18,49)@queries-SMTEncoding-7.smt2) Query-stats ... + + 2. Now, from a terminal, you run Z3 on this generated .smt2 file, + while passing it the following option and save the output in a + file. + + .. code-block:: none + + z3 queries-SMTEncoding-7.smt2 smt.qi.profile=true > sample_qiprofile + + 3. The output contains several lines that begin with + ``[quantifier_instances]``, which is what we're interested in. + + + .. code-block:: none + + grep quantifier_instances sample_qiprofile | sort -k 4 -n + + The last few lines of output look like this: + + .. code-block:: none + + [quantifier_instances] bool_inversion : 352 : 10 : 11 + [quantifier_instances] bool_typing : 720 : 10 : 11 + [quantifier_instances] constructor_distinct_BoxBool : 720 : 10 : 11 + [quantifier_instances] projection_inverse_BoxBool_proj_0 : 1772 : 10 : 11 + [quantifier_instances] primitive_Prims.op_Equality : 2873 : 10 : 11 + [quantifier_instances] int_typing : 3168 : 10 : 11 + [quantifier_instances] constructor_distinct_BoxInt : 3812 : 10 : 11 + [quantifier_instances] typing_SMTEncoding.factorial : 5490 : 10 : 11 + [quantifier_instances] int_inversion : 5506 : 11 : 12 + [quantifier_instances] @fuel_correspondence_SMTEncoding.factorial.fuel_instrumented : 5746 : 10 : 11 + [quantifier_instances] Prims_pretyping_ae567c2fb75be05905677af440075565 : 5835 : 11 : 12 + [quantifier_instances] projection_inverse_BoxInt_proj_0 : 6337 : 10 : 11 + [quantifier_instances] primitive_Prims.op_Multiply : 6394 : 10 : 11 + [quantifier_instances] primitive_Prims.op_Subtraction : 6394 : 10 : 11 + [quantifier_instances] token_correspondence_SMTEncoding.factorial.fuel_instrumented : 7629 : 10 : 11 + [quantifier_instances] @fuel_irrelevance_SMTEncoding.factorial.fuel_instrumented : 9249 : 10 : 11 + [quantifier_instances] equation_with_fuel_SMTEncoding.factorial.fuel_instrumented : 13185 : 10 : 10 + [quantifier_instances] refinement_interpretation_Tm_refine_542f9d4f129664613f2483a6c88bc7c2 : 15346 : 10 : 11 + [quantifier_instances] assumption_SMTEncoding.Factorial_unbounded : 15890 : 10 : 11 + + + Each line mentions is of the form: + + .. code-block:: none + + qid : number of instances : max generation : max cost + + where, + + * qid is the identifer of quantifier in the .smt2 file + + * the number of times it was instantiated, which is the + number we're most interested in + + * the generation and cost are other internal measures, which + Nikolaj Bjorner explains `here + `_ + + 4. Interpreting the results + + Clearly, as expected, ``assumption_SMTEncoding.Factorial_unbounded`` is + instantiated the most. + + Next, if you search in the .smt2 file for ":qid + refinement_interpretation_Tm_refine_542f9d4f129664613f2483a6c88bc7c2", + you'll find the assumption that gives an interpretation to the + ``HasType x Prims.nat`` predicate, where each instantiation of + ``Factorial_unbounded`` yields another instance of this fact. + + Notice that + ``equation_with_fuel_SMTEncoding.factorial.fuel_instrumented`` + is also instantiated a lot. This is because aside from the + matching loop due to ``HasType x Prims.nat``, each + instantiation of ``Factorial_unbounded`` also yields an + occurrence of ``factorial`` as a new active term, which the + solver then unrolls up to four times. + + We also see instantiations of quantifiers in ``Prims`` and other + basic facts like ``int_inversion``, ``bool_typing`` etc. + Sometimes, you may even find that these quantifiers fire the + most. However, these quantifiers are inherent to F*'s SMT + encoding: there's not much you can do about it as a user. They + are usually also not to blame for a slow proof---they fire a lot + when other terms are instantiated too much. You should try to + identify other quantifiers in your code or libraries that fire + a lot and try to understand the root cause of that. + +Z3 Axiom Profiler +~~~~~~~~~~~~~~~~~ + +The `Z3 Axiom Profiler +`_ can also be used to +find more detailed information about quantifier instantiation, +including which terms we used for instantiation, dependence among the +quantifiers in the form of instantiation chains, etc. + +However, there seem to be `some issues +`_ with +using it at the moment with Z3 logs generated from F*. + + +.. _Splitting_queries: + +Splitting Queries +................. + +In the next two sections, we look at a small example that Alex Rozanov +reported, shown below. It exhibits similar proof problems to our +artificial example with factorial. Instead of just identifying the +problematic quantifier, we look at how to remedy the performance +problem by revising the proof to be less reliant on Z3 quantifier +instantiation. + +.. literalinclude:: ../code/Alex.fst + :language: fstar + +The hypothesis is that ``unbounded f`` has exactly the same problem as +the our unbounded hypothesis on factorial---the ``forall/exists`` +quantifier contains a matching loop. + +This proof of ``find_above_for_g`` succeeds, but it takes a while and +F* reports: + +.. code-block:: none + + (Warning 349) The verification condition succeeded after splitting + it to localize potential errors, although the original non-split + verification condition failed. If you want to rely on splitting + queries for verifying your program please use the '--split_queries + always' option rather than relying on it implicitly. + +By default, F* collects all the proof obligations in a top-level F* +definition and presents it to Z3 in a single query with several +conjuncts. Usually, this allows Z3 to efficiently solve all the +conjuncts together, e.g., the proof search for one conjunct may yield +clauses useful to complete the search for other clauses. However, +sometimes, the converse can be true: the proof search for separate +conjuncts can interfere with each other negatively, leading to the +entire proof to fail even when every conjunct may be provable if tried +separately. Additionally, when F* calls Z3, it applies the current +rlimit setting for every query. If a query contains N conjuncts, +splitting the conjuncts into N separate conjuncts is effectively a +rlimit multiplier, since each query can separately consume resources +as much as the current rlimit. + +If the single query with several conjunct fails without Z3 reporting +any further information that F* can reconstruct into a localized error +message, F* splits the query into its conjuncts and tries each of +them in isolation, so as to isolate the failing conjunct it +any. However, sometimes, when tried in this mode, the proof of all +conjuncts can succeed. + +One way to respond to Warning 349 is to follow what it says and enable +``--split_queries always`` explicitly, at least for the program fragment in +question. This can sometimes stabilize a previously unstable +proof. However, it may also end up deferring an underlying +proof-performance problem. Besides, even putting stability aside, +splitting queries into their conjuncts results in somewhat slower +proofs. + +.. _UTH_opaque_to_smt: + +Taking Control of Quantifier Instantiations with Opaque Definitions +................................................................... + +Here is a revision of Alex's program that addresses the quantifier +instantiation problem. There are a few elements to the solution. + +.. literalinclude:: ../code/AlexOpaque.fst + :language: fstar + :start-after: //SNIPPET_START: opaque$ + :end-before: //SNIPPET_END: opaque$ + +1. Marking definitions as opaque + + The attribute ``[@@"opaque_to_smt"]`` on the definition of + ``unbounded`` instructs F* to not encode that definition to the SMT + solver. So, the problematic alternating quantifier is no longer + in the global scope. + +2. Selectively revealing the definition within a scope + + Of course, we still want to reason about the unbounded + predicate. So, we provide a lemma, ``instantiate_unbounded``, that + allows the caller to explicity instantiate the assumption + that ``f`` is unbounded on some lower bound ``m``. + + To prove the lemma, we use ``FStar.Pervasives.reveal_opaque``: + its first argument is the name of a symbol that should be + revealed; its second argument is a term in which that definition + should be revealed. It this case, it proves that ``unbounded f`` + is equal to ``forall m. exists n. abs (f n) > m``. + + With this fact available in the local scope, Z3 can prove the + lemma. You want to use ``reveal_opaque`` carefully, since with + having revealed it, Z3 has the problematic alternating quantifier + in scope and could go into a matching loop. But, here, since the + conclusion of the lemma is exactly the body of the quantifier, Z3 + quickly completes the proof. If even this proves to be + problematic, then you may have to resort to tactics. + +3. Explicitly instantiate where needed + + Now, with our instantiation lemma in hand, we can precisly + instantiate the unboundedness hypothesis on ``f`` as needed. + + In the proof, there are two instantiations, at ``m`` and ``m1``. + + Note, we are still relying on some non-trivial quantifier + instantiation by Z3. Notably, the two assertions are important to + instantiate the existential quantifier in the ``returns`` + clause. We'll look at that in more detail shortly. + + But, by making the problematic definition opaque and instantiating + it explicitly, our performance problem is gone---here's what + query-stats shows now. + + .. code-block:: none + + ((18,2-31,5)) Query-stats (AlexOpaque.find_above_for_g, 1) + succeeded in 46 milliseconds + +This `wiki page +`_ +provides more information on selectively revealing opaque definitions. + +Other Ways to Explicitly Trigger Quantifiers +............................................ + +For completeness, we look at some other ways in which quantifier +instantiation works. + +.. _Artificial_triggers: + +An Artificial Trigger +~~~~~~~~~~~~~~~~~~~~~ + +Instead of making the definition of ``unbounded`` opaque, we could +protect the universal quantifier with a pattern using some symbol +reserved for this purpose, as shown below. + +.. literalinclude:: ../code/AlexOpaque.fst + :language: fstar + :start-after: //SNIPPET_START: trigger$ + :end-before: //SNIPPET_END: trigger$ + + +1. We define a new function ``trigger x`` that is trivially true. + +2. In ``unbounded_alt`` we decorate the universal quantifier with an + explicit pattern, ``{:pattern (trigger x)}``. The pattern is not + semantically relevant---it's only there to control how the + quantifier is instantiated + +3. In ``find_above_for_gg``, whenever we want to instantiate the + quantifier with a particular lower bound ``k``, we assert ``trigger + k``. That gives Z3 an active term that mentions ``trigger`` which + it then uses to instantiate the quantifier with our choice of + ``k``. + +This style is not particularly pleasant, because it involves polluting +our definitions with semantically irrelevant triggers. The selectively +revealing opaque definitions style is much preferred. However, +artificial triggers can sometimes be useful. + +Existential quantifiers +~~~~~~~~~~~~~~~~~~~~~~~ + +We have an existential formula in the goal ``exists (i:nat). abs(g i) +> m`` and Z3 will try to solve this by finding an active term to +instantiate ``i``. In this case, the patterns Z3 picks is ``(g i)`` as +well the predicate ``(HasType i Prims.nat)``, which the SMT encoding +introduces. Note, F* does not currently allow the existential +quantifier in a ``returns`` annoation to be decorated with a +pattern---that will likely change in the future. + +Since ``g i`` is one of the patterns, by asserting ``abs (g (n - 1)) > +m`` in one branch, and ``abs (g (n1 - 1)) > m`` in the other, Z3 has +the terms it needs to instantiate the quantifier with ``n - 1`` in one +case, and ``n1 - 1`` in the other case. + +In fact, any assertion that mentions the ``g (n - 1)`` and +``g (n1 - 1)`` will do, even trivial ones, as the example below shows. + +.. literalinclude:: ../code/AlexOpaque.fst + :language: fstar + :start-after: //SNIPPET_START: trigger_exists$ + :end-before: //SNIPPET_END: trigger_exists$ + +We assert ``trigger (g (n - 1)))`` and ``trigger (g (n1 - 1))``, this +gives Z3 active terms for ``g (n - 1))`` and ``g (n1 - 1)``, which +suffices for the instantiation. Note, asserting ``trigger (n - 1)`` is +not enough, since that doesn't mention ``g``. + +However, recall that there's a second pattern that's also applicable +``(HasType i Prims.nat)``--we can get Z3 to instantiate the quantifier +if we can inject the predicate ``(HasType (n - 1) nat)`` into Z3's +context. By using ``trigger_nat``, as shown below, does the trick, +since F* inserts a proof obligation to show that the argument ``x`` in +``trigger_nat x`` validates ``(HasType x Prims.nat)``. + +.. literalinclude:: ../code/AlexOpaque.fst + :language: fstar + :start-after: //SNIPPET_START: trigger_nat$ + :end-before: //SNIPPET_END: trigger_nat$ + +Of course, rather than relying on implicitly chosen triggers for the +existentials, one can be explicit about it and provide the instance +directly, as shown below, where the ``introduce exists ...`` in each +branch directly provides the witness rather than relying on Z3 to find +it. This style is much preferred, if possible, than relying implicit +via various implicitly chosen patterns and artificial triggers. + +.. literalinclude:: ../code/AlexOpaque.fst + :language: fstar + :start-after: //SNIPPET_START: explicit_exists$ + :end-before: //SNIPPET_END: explicit_exists$ + +Here is `a link to the the full file <../code/AlexOpaque.fst>`_ with +all the variations we have explored. + +Overhead due to a Large Context +............................... + +Consider the following program: + +.. literalinclude:: ../code/ContextPollution.fst + :language: fstar + :start-after: //SNIPPET_START: context_test1$ + :end-before: //SNIPPET_END: context_test1$ + +The lemma ``test1`` is a simple property about ``FStar.Seq``, but the +lemma occurs in a module that also depends on a large number of other +modules---in this case, about 177 modules from the F* standard +library. All those modules are encoded to the SMT solver producing +about 11MB of SMT2 definitions with nearly 20,000 assertions for the +solver to process. This makes for a large search space for the solver +to explore to find a proof, however, most of those assertions are +quantified formulas guarded by patterns and they remain inert unless +some active term triggers them. Nevertheless, all these definitions +impose a noticeable overhead to the solver. If you turn +``--query_stats`` on (after a single warm-up query), it takes Z3 about +300 milliseconds (and about 3000 quantifier instantiations) to find a +proof for ``test1``. + +You probably won't really notice the overhead of a proof that takes +300 milliseconds---the F* standard library doesn't have many +quantifiers in scope with things like bad quantifier alternation that +lead to matching loops. However, as your development starts to depend +on an ever larger stack of modules, there's the danger that at some +point, your proofs are impacted by some bad choice of quantifiers in +some module that you have forgotten about. In that case, you may find +that seemingly simple proofs take many seconds to go through. In this +section, we'll look at a few things you can do to diagnose such +problems. + +Filtering the context +~~~~~~~~~~~~~~~~~~~~~ + +The first thing we'll look at is an F* option to remove facts from the +context. + +.. literalinclude:: ../code/ContextPollution.fst + :language: fstar + :start-after: //SNIPPET_START: using_facts$ + :end-before: //SNIPPET_END: using_facts$ + +The ``--using_facts_from`` option retains only facts from modules that +match the namespace-selector string provided. In this case, the +selector shrinks the context from 11MB and 20,000 assertions to around +1MB and 2,000 assertions and the query stats reports that the proof +now goes through in just 15 milliseconds---a sizeable speedup even +though the absolute numbers are still small. + +Of course, deciding which facts to filter from your context is not +easy. For example, if you had only retained ``FStar.Seq`` and forgot +to include ``Prims``, the proof would have failed. So, the +``--using_facts_from`` option isn't often very useful. + +Unsat Core and Hints +~~~~~~~~~~~~~~~~~~~~ + +When Z3 finds a proof, it can report which facts from the context were +relevant to the proof. This collection of facts is called the unsat +core, because Z3 has proven that the facts from the context and the +negated goal are unsatisfiable. F* has an option to record and replay +the unsat core for each query and F* refers to the recorded unsat cores +as "hints". + +Here's how to use hints: + + +1. Record hints + + .. code-block:: none + + fstar.exe --record_hints ContextPollution.fst + + This produces a file called ``ContextPollution.fst.hints`` + + The format of a hints file is internal and subject to change, + but it is a textual format and you can roughly see what it + contains. Here's a fragment from it: + + .. code-block:: none + + [ + "ContextPollution.test1", + 1, + 2, + 1, + [ + "@MaxIFuel_assumption", "@query", "equation_Prims.nat", + "int_inversion", "int_typing", "lemma_FStar.Seq.Base.lemma_eq_intro", + "lemma_FStar.Seq.Base.lemma_index_app1", + "lemma_FStar.Seq.Base.lemma_index_app2", + "lemma_FStar.Seq.Base.lemma_len_append", + "primitive_Prims.op_Addition", "primitive_Prims.op_Subtraction", + "projection_inverse_BoxInt_proj_0", + "refinement_interpretation_Tm_refine_542f9d4f129664613f2483a6c88bc7c2", + "refinement_interpretation_Tm_refine_ac201cf927190d39c033967b63cb957b", + "refinement_interpretation_Tm_refine_d83f8da8ef6c1cb9f71d1465c1bb1c55", + "typing_FStar.Seq.Base.append", "typing_FStar.Seq.Base.length" + ], + 0, + "3f144f59e410fbaa970cffb0e20df75d" + ] + + This is the hint entry for the query with whose id is + ``(ContextPollution.test1, 1)`` + + The next two fields are the fuel and ifuel used for the query, + ``2`` and ``1`` in this case. + + Then, we have the names of all the facts in the unsat core for this + query: you can see that it was only about 20 facts that were + needed, out of the 20,000 that were originally present. + + The second to last field is not used---it is always 0. + + And the last field is a hash of the query that was issued. + +2. Replaying hints + + The following command requests F* to search for + ``ContextPollution.fst.hints`` in the include path and when + attempting to prove a query with a given id, it looks for a hint + for that query in the hints file, uses the fuel and ifuel settings + present in the hints, and prunes the context to include only the + facts present in the unsat core. + + .. code-block:: none + + fstar.exe --use_hints ContextPollution.fst + + Using the hints usually improves verification times substantially, + but in this case, we see that the our proof now goes through in + about 130 milliseconds, not nearly as fast as the 15 milliseconds + we saw earlier. That's because when using a hint, each query to Z3 + spawns a new Z3 process initialized with just the facts in the + unsat core, and that incurs some basic start-up time costs. + +Many F* projects use hints as part of their build, including F*'s +standard library. The .hints files are checked in to the repository +and are periodically refreshed as proofs evolve. This helps improve +the stability of proofs: it may take a while for a proof to go +through, but once it does, you can record and replay the unsat core +and subsequent attempts of the same proof (or even small variations of +it) can go through quickly. + +Other projects do not use hints: some people (perhaps rightfully) see +hints as a way of masking underlying proof performance problems and +prefer to make proofs work quickly and robustly without hints. If you +can get your project to this state, without relying on hints, then so +much the better for you! + +Differential Profiling with qprofdiff +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you have a proof that takes very long without hints but goes +through quickly with hints, then the hints might help you diagnose why +the original proof was taking so long. This wiki page describes how to +`compare two Z3 quantifier instantiation profiles +`_ +with a tool that comes with Z3 called qprofdiff. + + +Hints that fail to replay +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sometimes, Z3 will report an unsat core, but when F* uses it to try to +replay a proof, Z3 will be unable to find a proof of unsat, and F* +will fall back to trying the proof again in its original context. The +failure to find a proof of unsat from a previously reported unsat core +is not a Z3 unsoundness or bug---it's because although the report core +is really logically unsat, finding a proof of unsat may have relied on +quantifier instantiation hints from facts that are not otherwise +semantically relevant. The following example illustrates. + +.. literalinclude:: ../code/HintReplay.fst + :language: fstar + + +Say you run the following: + +.. code-block:: none + + fstar --record_hints HintReplay.fst + fstar --query_stats --use_hints HintReplay.fst + +You will see the following output from the second run: + +.. code-block:: none + + (HintReplay.fst(15,27-15,39)) Query-stats (HintReplay.test, 1) failed + {reason-unknown=unknown because (incomplete quantifiers)} (with hint) + in 42 milliseconds .. + + (HintReplay.fst(15,27-15,39)) Query-stats (HintReplay.test, 1) succeeded + in 740 milliseconds ... + +The first attempt at the query failed when using the hint, and the +second attempt at the query (without the hint) succeeded. + +To see why, notice that to prove the assertion ``r x`` from the +hypothesis ``q x``, logically, the assumption ``Q_R`` +suffices. Indeed, if you look in the hints file, you will see that it +only mentions ``HintReplay.Q_R`` as part of the logical core. However, +``Q_R`` is guarded by a pattern ``p x`` and in the absence of the +assumption ``P_Q``, there is no way for the solver to derive an active +term ``p x`` to instantiate ``Q_R``---so, with just the unsat core, it +fails to complete the proof. + +Failures for hint replay usually point to some unusual quantifier +triggering pattern in your proof. For instance, here we used ``p x`` +as a pattern, even though ``p x`` doesn't appear anywhere in +``Q_R``---that's not usually a good choice, though sometimes, e.g., +when using :ref:`artificial triggers ` it can +come up. + +This `wiki page on hints +`_ +provides more information about diagnosing hint-replay failures, +particularly in the context of the Low* libraries. + + From c399a437da69d46e45a13a40e6ea88580c6f05b4 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 22 Dec 2023 18:43:45 -0800 Subject: [PATCH 03/65] mention vscode --- book/part1/part1_getting_off_the_ground.rst | 32 +++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/book/part1/part1_getting_off_the_ground.rst b/book/part1/part1_getting_off_the_ground.rst index eed91ba4427..ec1ba1d4ab5 100644 --- a/book/part1/part1_getting_off_the_ground.rst +++ b/book/part1/part1_getting_off_the_ground.rst @@ -16,18 +16,26 @@ F* can be used as a command line tool with any text editor. If you're viewing this in the interactive online tutorial, you can use the `Ace-based `_ text editor alongside, which provides some basic conveniences like syntax highlighting. However, -most advanced users of F* use emacs and `fstar-mode.el -`_, which provides several -utilities for interactively editing and checking F* files. We'll refer -to some features specific to fstar-mode.el as we go. - -fstar-mode.el relies on a generic but custom interaction protocol -implemented by the F* compiler. F* also provides a basic -implementation of the `Language Server Protocol -`_, which could -form the basis of integration with other editors. More information -about F*'s editor support can be found `here -`_. +beyond casual use, most users of F* rely on one of the follwing IDE +plugins. + + * `fstar-mode.el `_, + which provides several utilities for interactively editing and + checking F* files in emacs. + + * `fstar-vscode-assistant + `_, which + also provides interactive editing and checking support in VS Code. + +The main benefit to using these IDE plugins is that they allow you to +incrementally check just the changing suffix of an F* file, rather +than rechecking the entire file in batch mode. They also provide +standard things like jumping to definitions, type of a symbol etc. + +Both these plugins rely on a generic but custom interaction protocol +implemented by the F* compiler. It should be possible to implement IDE +support similar to fstar-mode.el or fstar-vscode-assistant in your +favorite plugin-capable editor. Basic syntactic structure From 9c22e96147aa87f366961fb612e576b7d4e04334 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 22 Dec 2023 18:44:21 -0800 Subject: [PATCH 04/65] pulse in progress --- book/Makefile | 5 +- book/conf.py | 1 + book/fstar_pygments.py | 93 ++++++++++++++++++++ book/index.rst | 1 + book/part4/part4_pure.rst | 1 + book/pulse/pulse.rst | 68 +++++++++++++++ book/pulse/pulse_ch1.rst | 175 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 343 insertions(+), 1 deletion(-) create mode 100755 book/pulse/pulse.rst create mode 100755 book/pulse/pulse_ch1.rst diff --git a/book/Makefile b/book/Makefile index 856c9853f75..c4a0c5be4ac 100644 --- a/book/Makefile +++ b/book/Makefile @@ -13,7 +13,10 @@ SOURCEDIR = . BUILDDIR = _build prep: - cp -R $(FSTAR_HOME)/doc/book/code . + mkdir -p code/pulse + # cp -R $(FSTAR_HOME)/doc/book/code/* code + cp -R $(STEEL_HOME)/share/steel/examples/pulse/by-example/* code/pulse + html: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/book/conf.py b/book/conf.py index ecd720c3164..99819275de8 100644 --- a/book/conf.py +++ b/book/conf.py @@ -22,6 +22,7 @@ lexers['fstar'] = fstar_pygments.CustomLexer() lexers['smt2'] = smt2_pygments.CustomLexer() +lexers['pulse'] = fstar_pygments.PulseLexer() def setup(app): app.add_css_file('custom.css') diff --git a/book/fstar_pygments.py b/book/fstar_pygments.py index d299ac6d143..001c56818d8 100644 --- a/book/fstar_pygments.py +++ b/book/fstar_pygments.py @@ -94,4 +94,97 @@ class CustomLexer(RegexLexer): ] } +class PulseLexer(RegexLexer): + name = 'Pulse' + aliases = ['pulse'] + filenames = ['*.fst', '*.fsti'] + keywords = ( + 'attributes' , + 'noeq' , + 'unopteq' , + 'and' , + 'assert' , + 'assume' , + 'begin' , + 'by' , + 'calc' , + 'class' , + 'decreases' , + 'Dv' , + 'effect' , + 'eliminate' , + 'else' , + 'end' , + 'ensures' , + 'exception' , + 'exists' , + 'false' , + 'friend' , + 'forall' , + 'fun' , + 'fn' , + 'function' , + 'GTot' , + 'if' , + 'in' , + 'include' , + 'inline' , + 'inline_for_extraction' , + 'instance' , + 'introduce' , + 'irreducible', + 'let' , + 'logic' , + 'match' , + 'module' , + 'new' , + 'new_effect' , + 'layered_effect' , + 'polymonadic_bind' , + 'polymonadic_subcomp' , + 'SMTPat' , + 'noextract', + 'of' , + 'open' , + 'opaque' , + 'private' , + 'range_of' , + 'rec' , + 'reifiable' , + 'reify' , + 'reflectable', + 'requires' , + 'returns' , + 'set_range_of', + 'sub_effect' , + 'synth' , + 'then' , + 'total' , + 'Tot' , + 'true' , + 'try' , + 'type' , + 'unfold' , + 'unfoldable' , + 'val' , + 'when' , + 'with' , + '_' , + 'Lemma' , + ) + tokens = { + 'root': [ + (r' ', Text), + (r'\n', Text), + (r'\r', Text), + (r'//.*\n', Comment), + (r'\([*]([^*]|[*]+[^)])*[*]+\)', Comment), + (words(keywords, suffix=r'\b'), Keyword), + (r'0x[0-9a-fA-F_]+', Literal.Number), + (r'[0-9_]+', Literal.Number), + (r'[a-zA-Z_]+', Text), + (r'.', Text), + ] + } + #class CustomFormatter: diff --git a/book/index.rst b/book/index.rst index b298e27156e..8a52ce31395 100644 --- a/book/index.rst +++ b/book/index.rst @@ -54,4 +54,5 @@ hope this book will soon include chapters authored by others. part3/part3 part4/part4 part5/part5 + pulse/pulse under_the_hood/under_the_hood diff --git a/book/part4/part4_pure.rst b/book/part4/part4_pure.rst index 335ebc30a8f..132b75b0274 100644 --- a/book/part4/part4_pure.rst +++ b/book/part4/part4_pure.rst @@ -59,6 +59,7 @@ calculi. If the reader is familiar with these, they may safely skip the next subsections, though even if you are an expert, if may be of interest to see how such program logics can be formalized in F*. +.. _Part4_Floyd_Hoare: A Primer on Floyd-Hoare Logic and Weakest Preconditions ------------------------------------------------------- diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst new file mode 100755 index 00000000000..ea9bab93a0a --- /dev/null +++ b/book/pulse/pulse.rst @@ -0,0 +1,68 @@ +.. _PartPulse: + +################################################################ +Pulse: Proof-oriented Programming in Concurrent Separation Logic +################################################################ + +Many F* projects involve building domain-specific languages with +specialized programming and proving support. For example, `Vale +`_ supports program proofs +for a structured assembly language; `Low* +`_ provides effectful +programming in F* with a C-like memory model; `EverParse +`_ is a DSL for writing +low-level parsers and serializers. Recently, F* has gained new +features for building DSLs embedded in F* with customized syntax, type +checker plugins, extraction support, etc., with *Pulse* as a showcase +example of such a DSL. + +Pulse is a new programming language embedded in F* and inheriting many +of its features (notably, it is higher order and has dependent types), +but with built-in support for programming with mutable state and +concurrency, with specifications and proofs in `Concurrent Separation +Logic `_. + +As a first taste of Pulse, here's a function to increment a mutable +integer reference. + +.. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst + :language: pulse + :start-after: ```pulse //incr + :end-before: ``` + +And here's a function to increment two references in parallel. + +.. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst + :language: pulse + :start-after: ```pulse //par_incr + :end-before: ``` + +You may not have heard about separation logic before---but perhaps +these specifications already make intuitive sense to you. The type of +``incr`` says that if "x points to 'i" initially, then when ``incr`` +returns, "x points to 'i + 1"; while ``par_incr`` increments the +contents of ``x`` and ``y`` in parallel by using the ``par`` +combinator. + +Concurrent separation logic is an active research area and there are +many such logics out there, all with different tradeoffs. Pulse's +logic is based on a logic called `SteelCore +`_ and a prior DSL built +on top of SteelCore called `Steel +`_. SteelCore itself builds +on ideas from `Iris `_ and `Hoare Type +Theory `_. But, you should not +need to know much about any of this research--- we'll start from the +basics and explain what you need to know about concurrent separation +logic to start programming and proving in Pulse. Additionally, Pulse +is an extension of F*, so all you've learned about F*, lemmas, +dependent types, refinement types, etc. will be of use again. + +.. .. image:: pulse_arch.png + + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + pulse_ch1 diff --git a/book/pulse/pulse_ch1.rst b/book/pulse/pulse_ch1.rst new file mode 100755 index 00000000000..6a3a227d7f5 --- /dev/null +++ b/book/pulse/pulse_ch1.rst @@ -0,0 +1,175 @@ +.. _Pulse_ByExample: + +Pulse Basics +============ + +A Pulse program is embedded in an F* program, where the Pulse parts +are delimited by a syntax extension marker "\`\`\`pulse ... \`\`\`", as +shown in the program below. + +.. literalinclude:: ../code/pulse/PulseByExample.fst + :language: pulse + :start-after: //SNIPPET_START: five + :end-before: //SNIPPET_END + +This program starts with a bit of regular F* defining ``fstar_five`` +followed by an a Pulse function ``five`` that references that F* +definition and proves that it always returns the constant +``5``. Finally, we have a bit of regular F* referencing the ``five`` +defined in Pulse. This is a really simple program, but it already +illustrates how Pulse and F* interact in both directions. + +In what follows, unless we really want to emphasize that a fragment of +code is Pulse embedded in a larger F* context, when showing Pulse +code, we'll just show the Pulse part, omitting the "\`\`\`pulse +... \`\`\`" delimiters. + + +A Separation Logic Primer +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Separation Logic was invented by John Reynolds, Peter O'Hearn, and +others in the late 1990s as a way to reason about imperative programs +that use shared, mutable data structures, e.g., linked lists and +graphs---see this paper for `an introduction to separation logic +`_. In the subsequent +decades, several innovations were added to separation logic by many +people, generalizing it beyond just sequential heap-manipulating +programs to distributed programs, concurrent programs, asynchronous +programs, etc. that manipulate abstract resources of various kinds, +including time and space, messages sent over communication channels, +etc. + +Much like other Hoare Logics, which we reviewed in :ref:`an earlier +section `, separation logic has two parts to +it. + +First, there is a language of propositions that describe properties +about program resources, e.g., the heap. These propositions have the +type ``vprop`` in Pulse, and under the covers in the SteelCore +semantics of Pulse, ``vprop = heap -> prop``. It is useful (at least +at first) to think of a ``vprop`` as a heap property, though we will +eventually treat it more abstractly and use it to model many other +kinds of resources. + +To connect ``vprop``'s to programs, separation logics also provide a +system of Hoare triples to describe the action of a program on the +heap. For example, the Hoare triple ``{ p } c { n. q }`` describes a +program ``c`` which when run in an initial heap ``h0`` satisfying ``p +h0`` (i.e., ``p`` is a precondition); ``c`` returns a value ``n`` +while transforming the heap to ``h1`` satisfying ``q n h1`` (i.e., +``q`` is a postcondition). Pulse's program logic is a +partial-correctness logic, meaning that ``c`` may also loop forever, +deadlock with other threads, etc. + +Here are some common ``vprops`` (defined in ``Pulse.Lib.Pervasives``): + + * ``emp``, the trivial proposition (equivalent to ``fun h -> True``). + + * ``pure p``, heap-independent predicate ``fun h -> p``. + +Let's go back to the program ``five``: + + * It is a function with a single unit argument---Pulse functions use + the keyword ``fn``. + + * The precondition is just ``emp``, the trivial assertion in + separation logic, i.e., ``five`` can be called in any initial + heap. + + * The return value is an integer ``n:int`` + + * The postcondition may refer to the name of the return value (``n`` + in this case) and here claims that the final heap satisfies the + ``pure`` proposition, ``n == 5``. + +In other words, the type signature in Pulse is a convenient way to +write the Hoare triple ``{ emp } five () { n:int. pure (n == 5) }``. + +At this point you may wonder if the postcondition of ``five`` is +actually strong enough. We've only said that the return value ``n == +5`` but have not said anything about the heap that results from +calling ``five ()``. Perhaps this specification allows ``five`` to +arbitrarily change any reference in the heap, since ``pure (5 == 5)`` +is true of any heap. [#]_ If you're familiar with Low*, Dafny, or +other languages based on Hoare logic for heaps, you may be wondering +about how come we haven't specified a ``modifies``-clause, describing +exactly which part of the heap a function may have changed. The nice +thing in separation logic is that there is no need to describe what +parts of the heap you may have modified. This is because a central +idea in logic is the concept of *ownership*. To a first approximation, +a computation can only access those resources that it is explicitly +granted access to in its precondition or those that it creates +itself. [#]_ In this case, with a precondition of ``emp``, the +function ``five`` does not have permission to access *any* resources, +and so ``five`` simply cannot modify the heap in any observable way. + +Let's go back to ``incr`` and ``par_incr`` that we saw in the previous +section and look at their types closely. We'll need to introduce two +more common ``vprop``'s, starting with the "points-to" predicate: + + * ``pts_to x v`` asserts that the reference ``x`` points to a cell + in the current heap that holds the value ``v``. + +``vprop``'s can also be combined in various ways, the most common one +being the "separating conjunction" or ``**``. + + * ``p ** q``, means that the heap can be split into two *disjoint* + fragments satisfying ``p`` and ``q``, respectively. Alternatively, + one could read ``p ** q`` as meaning that one holds the + permissions associated with both ``p`` and ``q`` separately in a + given heap. + +Now, perhaps the defining characteristic of separation logic is how +the ``**`` operator works in the program logic, via a key rule known +as the *frame* rule. The rule says that if you can prove the Hoare +triple ``{ p } c { n. q }``, then, for any other ``f : vprop``, you +can also prove ``{ p ** f } c { n. q ** f }``---``f`` is often called +the "frame". It might take some time to appreciate, but the frame rule +captures the essence of local, modular reasoning. Roughly, it states +that if a program is correct when it only has permission ``p`` on the +input heap, then it remains correct when run in a larger heap and is +guaranteed to preserve any property (``f``) on the part of the heap +that it doesn't touch. + +With this in mind, let's look again at the type of ``incr``, which +requires permission only to ``x`` and increments it: + +.. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst + :language: pulse + :start-after: ```pulse //incr + :end-before: ``` + +A point about the notation: The variable ``'i`` is an implicitly bound +logical variable, representing the value held in the ref-cell ``x`` in +the initial state. In this case, ``'i`` has type ``FStar.Ghost.erased +int``---we learned about :ref:`erased types in a previous section +`. One can also bind logical variables explicitly, e.g., +this is equivalent: + +.. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst + :language: pulse + :start-after: ```pulse //incr_explicit_i + :end-before: ``` + +Because of the frame rule, we can also call ``incr`` in a context like +``incr_frame`` below, and we can prove without any additional work +that ``y`` is unchanged. + +.. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst + :language: pulse + :start-after: ```pulse //incr_frame + :end-before: ``` + +In fact, Pulse lets use the frame rule with any ``f:vprop``, and we +get, for free, that ``incr x`` does not disturb ``f``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst + :language: pulse + :start-after: ```pulse //incr_frame_any + :end-before: ``` + +.. [#] For experts, Pulse's separation logic is *affine*. + +.. [#] When we get to things like invariants and locks, we'll see how + permissions can be acquired by other means. From ec132c78d894a465379909a98de4b3ecd76fba2e Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 22 Dec 2023 20:05:02 -0800 Subject: [PATCH 05/65] steel/pulse --- book/pulse/pulse.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index ea9bab93a0a..7939e596044 100755 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -58,6 +58,17 @@ logic to start programming and proving in Pulse. Additionally, Pulse is an extension of F*, so all you've learned about F*, lemmas, dependent types, refinement types, etc. will be of use again. + +.. note:: + + Why is it called Pulse? Because it's based on a logic called Steel, + and one of the authors and his daughter are big fans of a classic + reggae band called `Steel Pulse `_. We + wanted a name that was softer than Steel, more human, and, well, a + bit playful. So, Pulse! + + + .. .. image:: pulse_arch.png From 400e4a8627edb9a8518bee36c3684950cf9c0c62 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Wed, 27 Dec 2023 10:59:50 -0800 Subject: [PATCH 06/65] many small fixes, thanks to Brian Milnes --- book/fstar_pygments.py | 5 ++--- book/part1/part1_execution.rst | 9 ++++++--- book/part1/part1_getting_off_the_ground.rst | 16 ++++++++++----- book/part1/part1_inductives.rst | 11 +++++----- book/part2/part2_equality.rst | 2 +- book/part2/part2_inductive_type_families.rst | 6 ++++-- book/part2/part2_logical_connectives.rst | 21 +++++--------------- book/part2/part2_merkle.rst | 4 ++-- book/part2/part2_par.rst | 4 ++-- book/part2/part2_phoas.rst | 2 +- book/part2/part2_stlc.rst | 2 +- book/part2/part2_universes.rst | 4 ++-- book/part2/part2_vectors.rst | 7 ++++--- book/part2/part2_well_founded.rst | 2 +- book/part3/part3_interfaces.rst | 2 +- book/part3/part3_typeclasses.rst | 8 ++++++-- 16 files changed, 54 insertions(+), 51 deletions(-) diff --git a/book/fstar_pygments.py b/book/fstar_pygments.py index d299ac6d143..d81da466579 100644 --- a/book/fstar_pygments.py +++ b/book/fstar_pygments.py @@ -12,6 +12,7 @@ class CustomLexer(RegexLexer): 'unopteq' , 'and' , 'assert' , + 'assert_norm' , 'assume' , 'begin' , 'by' , @@ -87,9 +88,7 @@ class CustomLexer(RegexLexer): (r'//.*\n', Comment), (r'\([*]([^*]|[*]+[^)])*[*]+\)', Comment), (words(keywords, suffix=r'\b'), Keyword), - (r'0x[0-9a-fA-F_]+', Literal.Number), - (r'[0-9_]+', Literal.Number), - (r'[a-zA-Z_]+', Text), + (r'[a-zA-Z_0-9]+', Text), (r'.', Text), ] } diff --git a/book/part1/part1_execution.rst b/book/part1/part1_execution.rst index 07e9b5e745d..d5997682089 100644 --- a/book/part1/part1_execution.rst +++ b/book/part1/part1_execution.rst @@ -41,7 +41,7 @@ it describes the reduction strategy that F* used to interpret the term: * :math:`\zeta` means that recursive functions were unrolled We'll revisit what these reduction steps mean in a later chapter, -including how to customize it for your needs. +including how to customize them for your needs. Compiling to OCaml ^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ to write functions that actually have some effects. We'll learn a lot more about F*'s support for effectful program in a later section, but for now we'll just provide a glimpse of it by -showing (below) a ```main`` program that calls into our Quicksort +showing (below) a ``main`` program that calls into our Quicksort library. .. literalinclude:: ../code/Part1.Quicksort.Main.fst @@ -173,7 +173,10 @@ There are few things to note here: * ``FStar.IO.print_string`` is a library function that prints a string to ``stdout``. Its type is ``string -> ML unit``, a type - that we'll look at in detail when we learn more about effects. + that we'll look at in detail when we learn more about effects. For + now, keep in mind that functions with the ``ML`` label in their + type may have observable side effects, like IO, raising + exceptions, etc. * The end of the file contains ``let _ = main ()``, a top-level term that has a side-effect (printing to ``stdout``) when executed. In diff --git a/book/part1/part1_getting_off_the_ground.rst b/book/part1/part1_getting_off_the_ground.rst index ec1ba1d4ab5..9572a2f485c 100644 --- a/book/part1/part1_getting_off_the_ground.rst +++ b/book/part1/part1_getting_off_the_ground.rst @@ -20,8 +20,8 @@ beyond casual use, most users of F* rely on one of the follwing IDE plugins. * `fstar-mode.el `_, - which provides several utilities for interactively editing and - checking F* files in emacs. + which provides several utilities for interactively editing and + checking F* files in emacs. * `fstar-vscode-assistant `_, which @@ -49,7 +49,8 @@ from a client module. A module begins with the module's name (which must match the name of its file, i.e., ``module A`` is in ``A.fst``) and contains a sequence -of top-level signatures and definitions. +of top-level signatures and definitions. Module names always begin +with a capital letter. * Signatures ascribe a type to a definition, e.g., ``val f : t``. @@ -146,6 +147,11 @@ precedence. * ``>`` : Greater than (infix) * ``>=``: Greater than or equal (infix) +.. note:: + + F* follows the OCaml style of no negative integer literals, + instead negate a positive integer like ```(- 1)```. + .. _Part1_ch1_boolean_refinements: Boolean refinement types @@ -285,8 +291,8 @@ term like ``x + 1 : odd``. Functions ^^^^^^^^^ -To start writing interesting programs, we need a way to define -functions. In the core of F*, functions behave like functions in +We need a way to define functions to start writing interesting +programs. In the core of F*, functions behave like functions in maths. In other words, they are defined on their entire domain (i.e., they are total functions and always return a result) and their only observable behavior is the result they return (i.e., they don't have diff --git a/book/part1/part1_inductives.rst b/book/part1/part1_inductives.rst index e2b078b02d1..751bbfb583c 100644 --- a/book/part1/part1_inductives.rst +++ b/book/part1/part1_inductives.rst @@ -49,12 +49,11 @@ begin with an uppercase letter. | Two_of_three | Three_of_three - However, in general, as we start to use indexed types, each - constructor can build a different instance of the defined type, so - it will be important to have a way to specify the result type of - each constructor. For uniformity, throughout this book, we'll - always annotate the types of constructors, even when not strictly - necessary. + As we start to use indexed types, each constructor can build a + different instance of the defined type, so it will be important to + have a way to specify the result type of each constructor. For + uniformity, throughout this book, we'll always annotate the types + of constructors, even when not strictly necessary. F* can prove that they are distinct and that these are the only terms of type ``three``. diff --git a/book/part2/part2_equality.rst b/book/part2/part2_equality.rst index 0054f02c6c7..60ff65f244a 100644 --- a/book/part2/part2_equality.rst +++ b/book/part2/part2_equality.rst @@ -420,7 +420,7 @@ constructor of :math:`D` with arguments :math:`\overline{v:t_v}`, 2. Assume, for recursive types, for all :math:`\overline{q}`, :math:`\mathsf{hasEq}~(T~\overline{p}~\overline{q})`. -3. Prove, for all arguments :math:`\overline{v:t_v}`, prove :math:`\mathsf{hasEq}~t_v`. +3. For all arguments :math:`\overline{v:t_v}`, prove :math:`\mathsf{hasEq}~t_v`. If the proof in step 3 suceeds for all constructors, then F* introduces an axiom diff --git a/book/part2/part2_inductive_type_families.rst b/book/part2/part2_inductive_type_families.rst index ceeb87c4594..ec0f7afc170 100644 --- a/book/part2/part2_inductive_type_families.rst +++ b/book/part2/part2_inductive_type_families.rst @@ -9,6 +9,7 @@ following general structure. .. math:: \mathsf{type}~T_1~\overline{(x_1:p_1)} : \overline{y_1:q_1} \rightarrow \mathsf{Type} = \overline{| D_1 : t_1} \\ + \mathsf{and}~\ldots \\ \mathsf{and}~T_n~\overline{(x_n:p_n)} : \overline{y_n:q_n} \rightarrow \mathsf{Type} = \overline{| D_n : t_n} \\ This defines :math:`n` mutually inductive types, named :math:`T_1 \ldots @@ -74,6 +75,7 @@ Consider again the general shape of an inductive type definition: .. math:: \mathsf{type}~T_1~\overline{(x_1:p_1)} : \overline{y_1:q_1} \rightarrow \mathsf{Type} = \overline{| D_1 : t_1} \\ + \mathsf{and}~\ldots \\ \mathsf{and}~T_n~\overline{(x_n:p_n)} : \overline{y_n:q_n} \rightarrow \mathsf{Type} = \overline{| D_n : t_n} \\ This definition is strictly positive when @@ -130,8 +132,8 @@ reducing it produces an infinite chain of calls to ``loop' (Function loop')``. Admitting a non-positive definition like ``dyn`` has allowed us to build a non-terminating loop. -Such loops can also allow one to prove ``False``, as the next example -shows. +Such loops can also allow one to prove ``False`` (breaking soundness), +as the next example shows. .. literalinclude:: ../code/Part2.Positivity.fst :language: fstar diff --git a/book/part2/part2_logical_connectives.rst b/book/part2/part2_logical_connectives.rst index 5a024169bc9..78e5fee4746 100644 --- a/book/part2/part2_logical_connectives.rst +++ b/book/part2/part2_logical_connectives.rst @@ -128,7 +128,7 @@ proofs of ``p`` and ``q``, respectively. .. code-block:: - type pair (p q:Type) = | Pair : _1:p -> _1:q -> pair p q + type pair (p q:Type) = | Pair : _1:p -> _2:q -> pair p q .. note:: @@ -507,21 +507,10 @@ insantiation (as shown below), where the terms provided on the Its desugaring may be illuminating: -.. code-block:: fstar - - let forall_elim_2 (f: squash (forall (x0: t0) (x1: t1 x0). q x0 x1)) - (v0: t0) - (v1: t1 v0) - : squash (q v0 v1) - = FStar.Classical.Sugar.forall_elim - #(t1 v0) - #(fun x1 -> q v0 x1) - v1 - (FStar.Classical.Sugar.forall_elim - #t0 - #(fun x0 -> forall (x1: t1 x0). q x0 x1) - v0 - ()) +.. literalinclude:: ../code/Connectives.fst + :language: fstar + :start-after: //SNIPPET_START: forall_elim_2_desugar$ + :end-before: //SNIPPET_END: forall_elim_2_desugar$ .. _Part2_connectives_exists: diff --git a/book/part2/part2_merkle.rst b/book/part2/part2_merkle.rst index 1ac74c9c0c1..c41b6becd27 100644 --- a/book/part2/part2_merkle.rst +++ b/book/part2/part2_merkle.rst @@ -70,7 +70,7 @@ the leaf containing that item ``d``, and one can easily check by comparing hashes that the claimed path is accurate. In fact, we can prove that if a claimed path through the tree attests to the presence of some other item ``d' <> d``, then we can construct a collision on -the underlying hash algorithm---this property wil be our main proof of +the underlying hash algorithm---this property will be our main proof of security. @@ -197,7 +197,7 @@ other child node to the list of hashes. :start-after: //SNIPPET_START: prover :end-before: //SNIPPET_END: prover -In the cryptographic literature, this function is sometimes calles +In the cryptographic literature, this function is sometimes called *the prover*. A ``RES r ri hs`` is a claimed proof of the membership of ``r`` in the tree at the location specified by ``ri``. diff --git a/book/part2/part2_par.rst b/book/part2/part2_par.rst index fd2d5933b2d..1c952ba18ce 100644 --- a/book/part2/part2_par.rst +++ b/book/part2/part2_par.rst @@ -208,7 +208,7 @@ equivalence relation on ``m a`` * Left identity: ``bind (return x) f ~ f`` * Right identity: ``bind f return ~ f`` - * Associativity: ``bind f1 (fun x -> bind (f2 x) f3) ~ bind (bind f1 f2) g`` + * Associativity: ``bind f1 (fun x -> bind (f2 x) f3) ~ bind (bind f1 f2) f3`` Its easy to prove that ``st``, ``return``, and ``bind`` satisfy these laws in F*, where we pick the equivalence relation to equate functions @@ -285,7 +285,7 @@ Computation Trees, or Monads Generically Each time one defines a monad to model a computational effect, one usually thinks first of the effectful *actions* involved (e.g., reading and writing the state, or raising an error), and then finds a -way to pakage those actions into the interface of monad with +way to package those actions into the interface of monad with ``return`` and ``bind``, and then, to keep things honest, proves that the implementation satisfies the monad laws. diff --git a/book/part2/part2_phoas.rst b/book/part2/part2_phoas.rst index e362ab917e8..03451482f86 100644 --- a/book/part2/part2_phoas.rst +++ b/book/part2/part2_phoas.rst @@ -138,7 +138,7 @@ represented by F* variables and binders of the corresponding F* type * ``App``: To apply ``e1`` to ``e2`` in a well-typed way, we must prove that ``e1`` has an arrow type ``TArrow t1 t2``, while ``e2`` - has type ``t2``, and the resulting term ``App e1 e2`` has type + has type ``t1``, and the resulting term ``App e1 e2`` has type ``t2``. Notice how the indexing structure of the ``App`` constructor precisely captures this typing rule. diff --git a/book/part2/part2_stlc.rst b/book/part2/part2_stlc.rst index 3fe599ad3af..6b105c852d6 100644 --- a/book/part2/part2_stlc.rst +++ b/book/part2/part2_stlc.rst @@ -86,7 +86,7 @@ For example, the terms :math:`\lambda x. (\lambda x. x)` and :math:`x` is associated with the inner :math:`\lambda`; while :math:`\lambda x. (\lambda y. (\lambda z. x))` is represented as :math:`\lambda _. (\lambda _. (\lambda _. 2)`, since from the inner -occurrence of :math:`x` one must skip past :math:`2` :math:`\lambda`s +occurrence of :math:`x` one must skip past :math:`2` :math:`\lambda`'s to reach the :math:`\lambda` associated with :math:`x`. Note, the variable names are no longer significant in de Bruijn's notation. diff --git a/book/part2/part2_universes.rst b/book/part2/part2_universes.rst index cde7c247a3a..46ef177741c 100644 --- a/book/part2/part2_universes.rst +++ b/book/part2/part2_universes.rst @@ -512,7 +512,7 @@ perilously close to the unsound ``project`` axiom which enables paradoxes. This restriction is similar to Coq's restriction on its ``Prop`` type, -forbidding functions match on ``Prop`` to return results outside +forbidding functions that match on ``Prop`` to return results outside ``Prop``. The F* type ``prop`` (which we saw first :ref:`here `) is @@ -690,7 +690,7 @@ detect and fix such problems. Type u#(max uu___43588 uu___43589) is not a subtype of the expected type Type u#uu___43590 Meaning that the inferred type for the definition of ``tup2 a b`` is - ``Type u#(max a b)``, which is of course not the same a ``Type + ``Type u#(max a b)``, which is of course not the same as ``Type u#c``, and, sadly, the auto-generated fresh names in the error message don't make your life any easier. diff --git a/book/part2/part2_vectors.rst b/book/part2/part2_vectors.rst index b374ca1d364..c900c64ac7f 100644 --- a/book/part2/part2_vectors.rst +++ b/book/part2/part2_vectors.rst @@ -288,7 +288,7 @@ suffix starting at ``i``. -------------------------------------------------------------------------------- Write a tail-recursive version of ``split_at``. You will need a -``reverse`` function as a helper +``reverse`` function as a helper. .. container:: toggle @@ -317,8 +317,9 @@ Many texts about dependent types showcase length-indexed vectors, much as we've done here. Although useful as a simple illustrative example, the ``vec`` type we've seen is probably not what you want to use in practice. Especially in F*, where regular lists can easily be used -with refinement types, length-indexed vectors are redundant. The code -below shows how: +with refinement types, length-indexed vectors are redundant because we +simply refine our types using a ``length`` function. The code below +shows how: .. literalinclude:: ../code/LList.fst :language: fstar diff --git a/book/part2/part2_well_founded.rst b/book/part2/part2_well_founded.rst index c01b87789f6..bed21fd71f5 100644 --- a/book/part2/part2_well_founded.rst +++ b/book/part2/part2_well_founded.rst @@ -66,7 +66,7 @@ y``. Intuitively, this says that in order to build an instance of ``acc r x0``, you have to provide a function which can build a proof of ``acc r x1`` for all ``x1:a`` smaller than ``x0``. The only way to build such a function is one can avoid infinite regress, is if -thechain ``x0 r x1 r x2 r ...``, eventually terminates in some ``xn`` +the chain ``x0 r x1 r x2 r ...``, eventually terminates in some ``xn`` such that there are no elements smaller than it according to ``r``. In other words, if one can prove ``acc r x`` for all ``x:a``, then diff --git a/book/part3/part3_interfaces.rst b/book/part3/part3_interfaces.rst index 3da407607df..ac3e59b46a1 100644 --- a/book/part3/part3_interfaces.rst +++ b/book/part3/part3_interfaces.rst @@ -42,7 +42,7 @@ interpreted modulo ``2^32``. However, for many applications, one wants to program in a discipline that ensures that there is no unintentional arithmetic overflow, i.e., we'd like to use bounded integers for efficiency, and by proving that their operations don't overflow we can -reason bounded integer terms without using modular arithmetic. +reason about bounded integer terms without using modular arithmetic. .. note:: diff --git a/book/part3/part3_typeclasses.rst b/book/part3/part3_typeclasses.rst index 6442fe017d2..7a5053e15fe 100644 --- a/book/part3/part3_typeclasses.rst +++ b/book/part3/part3_typeclasses.rst @@ -290,6 +290,7 @@ while the remaining fields extend it with: * ``sub``: a bounded subtraction operation * ``lt`` : a comparison function * ``properties``, which show that + - ``add`` is related to integer addition ``+`` - ``sub`` is related to integer subtraction ``-`` - ``lt`` is related to ``<`` @@ -434,7 +435,7 @@ inheriting from a base class. :start-after: //SNIPPET_START: subtractable$ :end-before: //SNIPPET_END: subtractable$ -And here's another typeclass that, say, provides only the comparison +And here's another typeclass that provides only the comparison operation, also inheriting from the base class. .. literalinclude:: ../code/TypeclassesAlt2.fst @@ -659,7 +660,10 @@ F* infers the typeclass instantiations and the type of ``test`` to be In ``test2``, F* infers the type ``count_st s (op #monoid_nat_plus 0 (op #monoid_nat_plus 1 1)) unit``, and then automatically proves that this type is equivalent to the user annotation ``count_st s 2 unit``, -using the definition of ``monoid_nat_plus``. +using the definition of ``monoid_nat_plus``. Note, when one defines +``let+``, one can also use ``e1 ;+ e2`` to sequence computations when +the result type of ``e1`` is ``unit``. + Summary ------- From a5ada548a2ed179bf8d14899f0703d6f62a1b9d7 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Wed, 27 Dec 2023 11:03:06 -0800 Subject: [PATCH 07/65] fix some formatting --- book/part1/part1_getting_off_the_ground.rst | 2 +- book/part2/part2_inductive_type_families.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/book/part1/part1_getting_off_the_ground.rst b/book/part1/part1_getting_off_the_ground.rst index 9572a2f485c..f14fd8b89a0 100644 --- a/book/part1/part1_getting_off_the_ground.rst +++ b/book/part1/part1_getting_off_the_ground.rst @@ -150,7 +150,7 @@ precedence. .. note:: F* follows the OCaml style of no negative integer literals, - instead negate a positive integer like ```(- 1)```. + instead negate a positive integer like ``(- 1)``. .. _Part1_ch1_boolean_refinements: diff --git a/book/part2/part2_inductive_type_families.rst b/book/part2/part2_inductive_type_families.rst index ec0f7afc170..6139d7652ab 100644 --- a/book/part2/part2_inductive_type_families.rst +++ b/book/part2/part2_inductive_type_families.rst @@ -9,7 +9,7 @@ following general structure. .. math:: \mathsf{type}~T_1~\overline{(x_1:p_1)} : \overline{y_1:q_1} \rightarrow \mathsf{Type} = \overline{| D_1 : t_1} \\ - \mathsf{and}~\ldots \\ + \ldots\qquad\qquad\qquad\qquad\\ \mathsf{and}~T_n~\overline{(x_n:p_n)} : \overline{y_n:q_n} \rightarrow \mathsf{Type} = \overline{| D_n : t_n} \\ This defines :math:`n` mutually inductive types, named :math:`T_1 \ldots @@ -75,7 +75,7 @@ Consider again the general shape of an inductive type definition: .. math:: \mathsf{type}~T_1~\overline{(x_1:p_1)} : \overline{y_1:q_1} \rightarrow \mathsf{Type} = \overline{| D_1 : t_1} \\ - \mathsf{and}~\ldots \\ + \ldots\qquad\qquad\qquad\qquad\\ \mathsf{and}~T_n~\overline{(x_n:p_n)} : \overline{y_n:q_n} \rightarrow \mathsf{Type} = \overline{| D_n : t_n} \\ This definition is strictly positive when From 726a1938944522550f249fcfbb655ba474fc91ce Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Wed, 27 Dec 2023 11:07:15 -0800 Subject: [PATCH 08/65] typo --- book/part3/part3_typeclasses.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part3/part3_typeclasses.rst b/book/part3/part3_typeclasses.rst index 7a5053e15fe..3dccca09782 100644 --- a/book/part3/part3_typeclasses.rst +++ b/book/part3/part3_typeclasses.rst @@ -670,7 +670,7 @@ Summary Typeclasses are a flexible way to structure programs in an abstract and generic style. Not only can this make program construction more -modular, in can also make proofs and reasoning more abstract, +modular, it can also make proofs and reasoning more abstract, particularly when typeclasses contain not just methods but also properties characterizing how those methods ought to behave. Reasoning abstractly can make proofs simpler: for example, if the monoid-ness of From 5a0eec89c30320a600f51249c4e19882aa1f48a2 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 29 Dec 2023 21:57:17 -0800 Subject: [PATCH 09/65] ch2 in progress --- book/pulse/pulse_ch2.rst | 198 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100755 book/pulse/pulse_ch2.rst diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst new file mode 100755 index 00000000000..6353192b287 --- /dev/null +++ b/book/pulse/pulse_ch2.rst @@ -0,0 +1,198 @@ +.. _Pulse_References: + +Mutable References +================== + +Pulse aims to support programming with explicit control over memory +management and without need for a garbage collector, similar to +languages like C or Rust, but, of course, in a proof-oriented style. +Towards that end, one of the main features it offers (especially in +comparison to purely functional F*) is support for references to +mutable memory. In this chapter, we'll learn about three kinds of +mutable references: stack references, heap references (or boxes), and +ghost references. Stack references point to memory allocated in the +stack frame of the current function (in which case the memory is +reclaimed when the function returns). Heap references, or boxes, point +to memory locations in the heap, and heap memory is explicitly +reclaimed by calling ``drop`` or ``free``. Ghost references are for +specification and proof purposes only and point to memory locations +that do not really exist at runtime. + + +Stack references +^^^^^^^^^^^^^^^^ + +We have already seen the type ``ref t``, as in the ``incr`` function +below: the type ``ref t`` is the type of a memory reference that +typically refers to a memory location on the stack (though, we'll soon +see that it may also be used to refer to memory on the heap). + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //incr + :end-before: ``` + + +``let mut`` creates a new stack ref +................................... + +To create a new ``ref t``, one uses the ``let mut`` construct of +Pulse, as shown below. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //one + :end-before: ``` + +The body of the program is annotated to show program assertions that +are true after each command. + + * Initially, only the precondition ``emp`` is valid. + + * After ``let mut i = 0``, we have ``i : ref int`` and ``pts_to i + 0``, meaning that ``i`` points to a stack slot that holds the + value ``0``. + + * After calling ``incr i``, we have ``pts_to i (0 + 1)`` + + * Finally, we dereference ``i`` using ``!i`` and return ``v:int`` + the current value of ``i`` at which point the scope of ``i`` ends + and the memory it points to is reclaimed. The final assertion + corresponds to the postcondition of ``one``. + +A few additional points to note here: + + * Pulse proves ``pure`` properties automatically, by sending queries + to the SMT solver. + + * Pulse simplifies ``vprop`` implicitly, e.g., Pulse will + automatically rewrite ``emp ** p`` to ``p``. + + * Like F*, Pulse tries to instantiate implicit arguments + automatically, e.g., at the call to ``incr``, Pulse automatically + instantiates ``'v`` to ``0`` (actually, to ``hide 0``). + +Stack references are scoped and implicitly reclaimed +.................................................... + +To emphasize that stack references allocted with ``let mut`` are +scoped, let's look at the program below that Pulse refuses to check: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //refs_as_scoped FAIL + :end-before: ``` + +The error points to the location of ``s`` with the message below, +meaning that the current assertion on the heap is only ``emp``, while +the goal to be proven for the postcondition is ``pts_to s 0``. In +other words, we no longer have ownership on ``s`` once it goes out of +scope. + +.. code-block:: + + - Cannot prove: + pts_to s 0 + - In the context: + emp + +Dereferencing is explicit +......................... + +Unlike languages like C or Rust which make a distinction between +l-values and r-values and implicitly read the content of references, +in Pulse (like in OCaml), references are explicitly dereferenced. [#]_ +As the program below illustrates, references themselves can be passed +to other functions (e.g., as in/out-parameters) while their current +values must be passed expliclity. + +The function ``add`` takes both a refrence ``r:ref int`` and a value +``n:int`` as arguments: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //add + :end-before: ``` + +Meanwhile, the function ``four`` allocates a new stack reference ``i`` +initialized to ``1``; calls ``add`` twice to double the value stored +in ``i`` each time. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //four + :end-before: ``` + +Note, Pulse expects the results of all stateful operations to be +explicitly ``let``-bound. For example, the following code fails to +type checked: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //four FAIL + :end-before: ``` + +.. code-block:: + + - Expected type "int"; but "!i" has type + "stt int + (pts_to i (reveal (*?u93*) _)) + (fun x -> pts_to i x ** pure (reveal (*?u93*) _ == x))" + +The error points to the first occurrence of ``(!i)``. The message is +admittedly cryptic and should be improved. As we'll see in a later +chapter, the type ``stt _ _ _`` is the type of an unevaluated Pulse +comptuation---this error complains that ``add`` expected an argument +of type ``int`` but instead got an unevaluated computation. + +Erased values are for specification and proof only +.................................................. + +Let's take a closer look at how dereferencing works in the function +``value_of`` below: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //value_of$ + :end-before: ``` + +Its slightly more explicit form is shown below, where ``w:erased a`` +is an erased value witnessing the current contents referenced by +``r``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //value_of_explicit$ + :end-before: ``` + +Notice how the precondition requires ``pts_to r w`` while the +postcondition changes to ``pts_to r v``, along with the property that +``v == reveal w``, i.e., the type proves that if we read the reference +the value we get is equal to the logical witness provided. + +The logical witness is an erased value, so one cannot directly use it +in a non-ghost computation. For example, if instead of reading the +reference, we attempt to just return ``reveal w``, the +code fails to check with the error shown below. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //value_of_explicit_fail$ + :end-before: ``` + +.. code-block:: + + Expected a Total computation, but got Ghost + + +Heap references +^^^^^^^^^^^^^^^ + + +Ghost references +^^^^^^^^^^^^^^^^ + + +.. [#] We are considering switching this convention and the Pulse + option ``--ext 'pulse:rvalues'`` can be enabled to add implicit + dereferencing; however, it is not yet recommended for use. From 6cb6dc8813533aff6ebfb35eb51ef6622d16c929 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sat, 30 Dec 2023 14:16:53 -0800 Subject: [PATCH 10/65] ch2 wip --- book/fstar_pygments.py | 234 +++++++++---------------- book/pulse/pulse.rst | 1 + book/pulse/pulse_ch1.rst | 195 +++++++++++++-------- book/pulse/pulse_ch2.rst | 368 +++++++++++++++++++++++++++++---------- 4 files changed, 485 insertions(+), 313 deletions(-) diff --git a/book/fstar_pygments.py b/book/fstar_pygments.py index 822c40601de..0177732c699 100644 --- a/book/fstar_pygments.py +++ b/book/fstar_pygments.py @@ -1,85 +1,86 @@ from pygments.lexer import RegexLexer, words from pygments.token import * +fstar_keywords = ( + 'attributes' , + 'noeq' , + 'unopteq' , + 'and' , + 'assert' , + 'assert_norm' , + 'assume' , + 'begin' , + 'by' , + 'calc' , + 'class' , + 'decreases' , + 'Dv' , + 'effect' , + 'eliminate' , + 'else' , + 'end' , + 'ensures' , + 'exception' , + 'exists' , + 'false' , + 'friend' , + 'forall' , + 'fun' , + 'function' , + 'GTot' , + 'if' , + 'in' , + 'include' , + 'inline' , + 'inline_for_extraction' , + 'instance' , + 'introduce' , + 'irreducible', + 'let' , + 'logic' , + 'match' , + 'module' , + 'new' , + 'new_effect' , + 'layered_effect' , + 'polymonadic_bind' , + 'polymonadic_subcomp' , + 'SMTPat' , + 'noextract', + 'of' , + 'open' , + 'opaque' , + 'private' , + 'range_of' , + 'rec' , + 'reifiable' , + 'reify' , + 'reflectable', + 'requires' , + 'returns' , + 'set_range_of', + 'sub_effect' , + 'synth' , + 'then' , + 'total' , + 'Tot' , + 'true' , + 'try' , + 'type' , + 'unfold' , + 'unfoldable' , + 'val' , + 'when' , + 'with' , + '_' , + 'Lemma' , +) + # very rough lexer; not 100% precise class CustomLexer(RegexLexer): name = 'FStar' aliases = ['fstar'] filenames = ['*.fst', '*.fsti'] - keywords = ( - 'attributes' , - 'noeq' , - 'unopteq' , - 'and' , - 'assert' , - 'assert_norm' , - 'assume' , - 'begin' , - 'by' , - 'calc' , - 'class' , - 'decreases' , - 'Dv' , - 'effect' , - 'eliminate' , - 'else' , - 'end' , - 'ensures' , - 'exception' , - 'exists' , - 'false' , - 'friend' , - 'forall' , - 'fun' , - 'function' , - 'GTot' , - 'if' , - 'in' , - 'include' , - 'inline' , - 'inline_for_extraction' , - 'instance' , - 'introduce' , - 'irreducible', - 'let' , - 'logic' , - 'match' , - 'module' , - 'new' , - 'new_effect' , - 'layered_effect' , - 'polymonadic_bind' , - 'polymonadic_subcomp' , - 'SMTPat' , - 'noextract', - 'of' , - 'open' , - 'opaque' , - 'private' , - 'range_of' , - 'rec' , - 'reifiable' , - 'reify' , - 'reflectable', - 'requires' , - 'returns' , - 'set_range_of', - 'sub_effect' , - 'synth' , - 'then' , - 'total' , - 'Tot' , - 'true' , - 'try' , - 'type' , - 'unfold' , - 'unfoldable' , - 'val' , - 'when' , - 'with' , - '_' , - 'Lemma' , - ) tokens = { 'root': [ (r' ', Text), @@ -87,90 +88,20 @@ class CustomLexer(RegexLexer): (r'\r', Text), (r'//.*\n', Comment), (r'\([*]([^*]|[*]+[^)])*[*]+\)', Comment), - (words(keywords, suffix=r'\b'), Keyword), + (words(fstar_keywords, suffix=r'\b'), Keyword), (r'[a-zA-Z_0-9]+', Text), (r'.', Text), ] } +pulse_keywords = ( + "fn", +) + class PulseLexer(RegexLexer): name = 'Pulse' aliases = ['pulse'] filenames = ['*.fst', '*.fsti'] - keywords = ( - 'attributes' , - 'noeq' , - 'unopteq' , - 'and' , - 'assert' , - 'assume' , - 'begin' , - 'by' , - 'calc' , - 'class' , - 'decreases' , - 'Dv' , - 'effect' , - 'eliminate' , - 'else' , - 'end' , - 'ensures' , - 'exception' , - 'exists' , - 'false' , - 'friend' , - 'forall' , - 'fun' , - 'fn' , - 'function' , - 'GTot' , - 'if' , - 'in' , - 'include' , - 'inline' , - 'inline_for_extraction' , - 'instance' , - 'introduce' , - 'irreducible', - 'let' , - 'logic' , - 'match' , - 'module' , - 'new' , - 'new_effect' , - 'layered_effect' , - 'polymonadic_bind' , - 'polymonadic_subcomp' , - 'SMTPat' , - 'noextract', - 'of' , - 'open' , - 'opaque' , - 'private' , - 'range_of' , - 'rec' , - 'reifiable' , - 'reify' , - 'reflectable', - 'requires' , - 'returns' , - 'set_range_of', - 'sub_effect' , - 'synth' , - 'then' , - 'total' , - 'Tot' , - 'true' , - 'try' , - 'type' , - 'unfold' , - 'unfoldable' , - 'val' , - 'when' , - 'with' , - '_' , - 'Lemma' , - ) tokens = { 'root': [ (r' ', Text), @@ -178,9 +109,8 @@ class PulseLexer(RegexLexer): (r'\r', Text), (r'//.*\n', Comment), (r'\([*]([^*]|[*]+[^)])*[*]+\)', Comment), - (words(keywords, suffix=r'\b'), Keyword), - (r'0x[0-9a-fA-F_]+', Literal.Number), - (r'[0-9_]+', Literal.Number), + (words(fstar_keywords, suffix=r'\b'), Keyword), + (words(pulse_keywords, suffix=r'\b'), Keyword), (r'[a-zA-Z_]+', Text), (r'.', Text), ] diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 7939e596044..a913359dd35 100755 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -77,3 +77,4 @@ dependent types, refinement types, etc. will be of use again. :caption: Contents: pulse_ch1 + pulse_ch2 diff --git a/book/pulse/pulse_ch1.rst b/book/pulse/pulse_ch1.rst index 6a3a227d7f5..0ea34b76616 100755 --- a/book/pulse/pulse_ch1.rst +++ b/book/pulse/pulse_ch1.rst @@ -1,4 +1,4 @@ -.. _Pulse_ByExample: +.. _Pulse_Basics: Pulse Basics ============ @@ -41,85 +41,102 @@ including time and space, messages sent over communication channels, etc. Much like other Hoare Logics, which we reviewed in :ref:`an earlier -section `, separation logic has two parts to -it. +section `, separation logic comes in two parts. -First, there is a language of propositions that describe properties -about program resources, e.g., the heap. These propositions have the -type ``vprop`` in Pulse, and under the covers in the SteelCore -semantics of Pulse, ``vprop = heap -> prop``. It is useful (at least -at first) to think of a ``vprop`` as a heap property, though we will -eventually treat it more abstractly and use it to model many other -kinds of resources. +**Separation Logic Propositions** First, we have a language of +propositions that describe properties about program resources, e.g., +the heap. These propositions have the type ``vprop`` in Pulse, [#]_ +and, under the covers in the SteelCore semantics of Pulse, a ``vprop = +state -> prop``, where ``state`` represents the state of a program, +e.g., the conents of memory. It is useful (at least at first) to think +of a ``vprop`` as a memory property, though we will eventually treat +it more abstractly and use it to model many other kinds of resources. -To connect ``vprop``'s to programs, separation logics also provide a -system of Hoare triples to describe the action of a program on the -heap. For example, the Hoare triple ``{ p } c { n. q }`` describes a -program ``c`` which when run in an initial heap ``h0`` satisfying ``p -h0`` (i.e., ``p`` is a precondition); ``c`` returns a value ``n`` -while transforming the heap to ``h1`` satisfying ``q n h1`` (i.e., -``q`` is a postcondition). Pulse's program logic is a -partial-correctness logic, meaning that ``c`` may also loop forever, -deadlock with other threads, etc. +.. I'm calling it a hmem to not confuse things with heap vs stack + later. -Here are some common ``vprops`` (defined in ``Pulse.Lib.Pervasives``): +**Separation Logic Hoare Triples** To connect ``vprop``'s to programs, +separation logics use Hoare triples to describe the action of a +program on its state. For example, the Hoare triple ``{ p } c { n. q +}`` describes a program ``c`` which when run in an initial state +``s0`` satisfying ``p s0`` (i.e., ``p`` is a precondition); ``c`` +returns a value ``n`` while transforming the state to ``s1`` +satisfying ``q n s1`` (i.e., ``q`` is a postcondition). Pulse's +program logic is a partial-correctness logic, meaning that ``c`` may +also loop forever, deadlock with other threads, etc. - * ``emp``, the trivial proposition (equivalent to ``fun h -> True``). +**Some simple vprops and triples**: Here are two of the simplest + ``vprops`` (defined in ``Pulse.Lib.Pervasives``): - * ``pure p``, heap-independent predicate ``fun h -> p``. + * ``emp``, the trivial proposition (equivalent to ``fun s -> True``). -Let's go back to the program ``five``: + * ``pure p``, heap-independent predicate ``fun s -> p``. ``emp`` is + equivalent to ``pure True``. + +The type of the program ``five`` illustrates how these ``vprop``'s are +used in program specifications: * It is a function with a single unit argument---Pulse functions use the keyword ``fn``. * The precondition is just ``emp``, the trivial assertion in separation logic, i.e., ``five`` can be called in any initial - heap. + state. * The return value is an integer ``n:int`` * The postcondition may refer to the name of the return value (``n`` - in this case) and here claims that the final heap satisfies the + in this case) and here claims that the final state satisfies the ``pure`` proposition, ``n == 5``. In other words, the type signature in Pulse is a convenient way to write the Hoare triple ``{ emp } five () { n:int. pure (n == 5) }``. -At this point you may wonder if the postcondition of ``five`` is -actually strong enough. We've only said that the return value ``n == -5`` but have not said anything about the heap that results from -calling ``five ()``. Perhaps this specification allows ``five`` to -arbitrarily change any reference in the heap, since ``pure (5 == 5)`` -is true of any heap. [#]_ If you're familiar with Low*, Dafny, or -other languages based on Hoare logic for heaps, you may be wondering -about how come we haven't specified a ``modifies``-clause, describing -exactly which part of the heap a function may have changed. The nice -thing in separation logic is that there is no need to describe what -parts of the heap you may have modified. This is because a central -idea in logic is the concept of *ownership*. To a first approximation, -a computation can only access those resources that it is explicitly -granted access to in its precondition or those that it creates -itself. [#]_ In this case, with a precondition of ``emp``, the -function ``five`` does not have permission to access *any* resources, -and so ``five`` simply cannot modify the heap in any observable way. - -Let's go back to ``incr`` and ``par_incr`` that we saw in the previous -section and look at their types closely. We'll need to introduce two -more common ``vprop``'s, starting with the "points-to" predicate: +**Ownership** At this point you may wonder if the postcondition of +``five`` is actually strong enough. We've only said that the return +value ``n == 5`` but have not said anything about the state that +results from calling ``five ()``. Perhaps this specification allows +``five`` to arbitrarily change any memory location in the state, since ``pure +(5 == 5)`` is true of any state. [#]_ If you're familiar with Low*, +Dafny, or other languages based on Hoare logic for heaps, you may be +wondering about how come we haven't specified a ``modifies``-clause, +describing exactly which part of the state a function may have +changed. The nice thing in separation logic is that there is no need +to describe what parts of the state you may have modified. This is +because a central idea in logic is the concept of *ownership*. To a +first approximation, a computation can only access those resources +that it is explicitly granted access to in its precondition or those +that it creates itself. [#]_ In this case, with a precondition of +``emp``, the function ``five`` does not have permission to access +*any* resources, and so ``five`` simply cannot modify the state in any +observable way. + + +**Separating Conjunction and the Frame Rule** Let's go back to +``incr`` and ``par_incr`` that we saw in the previous section and look +at their types closely. We'll need to introduce two more common +``vprop``'s, starting with the "points-to" predicate: * ``pts_to x v`` asserts that the reference ``x`` points to a cell - in the current heap that holds the value ``v``. + in the current state that holds the value ``v``. ``vprop``'s can also be combined in various ways, the most common one -being the "separating conjunction" or ``**``. +being the "separating conjunction", written ``**`` in Pulse. [#]_ - * ``p ** q``, means that the heap can be split into two *disjoint* + * ``p ** q``, means that the state can be split into two *disjoint* fragments satisfying ``p`` and ``q``, respectively. Alternatively, one could read ``p ** q`` as meaning that one holds the permissions associated with both ``p`` and ``q`` separately in a - given heap. + given state. The ``**`` operator satisfies the following laws: + + - Commutativity: ``p ** q`` is equivalent to ``q * p`` + - Associativity: ``p ** (q ** r)`` is equivalent to ``(p ** q) ** r`` + + - Left and right unit: ``p ** emp`` is equivalent to ``p``. Since + ``**`` is commutative, this also means that ``emp ** p`` is + equivalent to ``p`` + Now, perhaps the defining characteristic of separation logic is how the ``**`` operator works in the program logic, via a key rule known as the *frame* rule. The rule says that if you can prove the Hoare @@ -128,8 +145,8 @@ can also prove ``{ p ** f } c { n. q ** f }``---``f`` is often called the "frame". It might take some time to appreciate, but the frame rule captures the essence of local, modular reasoning. Roughly, it states that if a program is correct when it only has permission ``p`` on the -input heap, then it remains correct when run in a larger heap and is -guaranteed to preserve any property (``f``) on the part of the heap +input state, then it remains correct when run in a larger state and is +guaranteed to preserve any property (``f``) on the part of the state that it doesn't touch. With this in mind, let's look again at the type of ``incr``, which @@ -140,22 +157,10 @@ requires permission only to ``x`` and increments it: :start-after: ```pulse //incr :end-before: ``` -A point about the notation: The variable ``'i`` is an implicitly bound -logical variable, representing the value held in the ref-cell ``x`` in -the initial state. In this case, ``'i`` has type ``FStar.Ghost.erased -int``---we learned about :ref:`erased types in a previous section -`. One can also bind logical variables explicitly, e.g., -this is equivalent: - -.. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst - :language: pulse - :start-after: ```pulse //incr_explicit_i - :end-before: ``` - Because of the frame rule, we can also call ``incr`` in a context like ``incr_frame`` below, and we can prove without any additional work that ``y`` is unchanged. - + .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst :language: pulse :start-after: ```pulse //incr_frame @@ -163,13 +168,67 @@ that ``y`` is unchanged. In fact, Pulse lets use the frame rule with any ``f:vprop``, and we get, for free, that ``incr x`` does not disturb ``f``. - + .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst :language: pulse :start-after: ```pulse //incr_frame_any :end-before: ``` -.. [#] For experts, Pulse's separation logic is *affine*. - +A point about the notation: The variable ``'i`` is an implicitly bound +logical variable, representing the value held in the ref-cell ``x`` in +the initial state. In this case, ``'i`` has type ``FStar.Ghost.erased +int``---we learned about :ref:`erased types in a previous section +`. One can also bind logical variables explicitly, e.g., +this is equivalent: + +.. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst + :language: pulse + :start-after: ```pulse //incr_explicit_i + :end-before: ``` + +**Other vprop connectives** In addition the separating conjunction, +Pulse, like other separation logics, provides other ways to combine +``vprops``. We'll look at these in detail in the subsequent chapters, +but we list the most common other connectives below just to give you a +taste of the logic. + + * ``exists* (x1:t1) ... (xn:tn). p``: Existential quantification is + used extensively in the Pulse libraries, and the language provides + many tools to make existentials convenient to use. ``exists x. p`` + is valid in a state ``s`` if there is a witness ``w`` such that + ``p [w/x]`` is valid in ``s``. For experts, existential + quantification is impredicate, in the sense that one can quantify + over ``vprops`` themselves, i.e., ``exists* (p:vprop). q`` is + allowed. + + * ``forall* (x1:t1) ... (xn:tn). p``: Universal quantification is + also supported, though less commonly used. ``forall (x:t). p`` is + valid in ``s`` if ``p[w/x]`` is valid for all values ``w:t``. + Like existential quantification, it is also impredicative. + + * ``p @==> q`` is a form of separating implication similar to an + operator called a *magic wand* or a *view shift* in other + separation logics. + +Pulse does not yet provide libraries for conjunction or +disjunction. However, since Pulse is embedded in F*, new vprops can +also be defined by the user and it is common to do so, e.g., +recursively defined predicates, or variants of the connectives +described above. + +.. [#] They are called ``vprop`` for mostly historical reasons. A + version of the Steel separation logic on which Pulse is based + allows associating a *value* with a separation logic + proposition, so these came to be known as "value" + propositions. However, Pulse does not make use of this + feature---perhaps a better name in Pulse would be ``slprop``, + for separation logic proposition. + +.. [#] For experts, Pulse's separation logic is *affine*. + .. [#] When we get to things like invariants and locks, we'll see how permissions can be acquired by other means. + +.. [#] In the separation logic literature, separating conjunction is + written ``p * q``, with just a single star. We use two stars + ``**`` to avoid a clash with multiplication. diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index 6353192b287..a1bd6d586d1 100755 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -8,30 +8,254 @@ management and without need for a garbage collector, similar to languages like C or Rust, but, of course, in a proof-oriented style. Towards that end, one of the main features it offers (especially in comparison to purely functional F*) is support for references to -mutable memory. In this chapter, we'll learn about three kinds of -mutable references: stack references, heap references (or boxes), and -ghost references. Stack references point to memory allocated in the -stack frame of the current function (in which case the memory is -reclaimed when the function returns). Heap references, or boxes, point -to memory locations in the heap, and heap memory is explicitly -reclaimed by calling ``drop`` or ``free``. Ghost references are for -specification and proof purposes only and point to memory locations -that do not really exist at runtime. +mutable memory that can be both allocated and reclaimed. + +In this chapter, we'll learn about three kinds of mutable references: +stack references, heap references (or boxes), and ghost +references. Stack references point to memory allocated in the stack +frame of the current function (in which case the memory is reclaimed +when the function returns). Heap references, or boxes, point to memory +locations in the heap, and heap memory is explicitly reclaimed by +calling ``drop`` or ``free``. Ghost references are for specification +and proof purposes only and point to memory locations that do not +really exist at runtime. + +``ref t``: Stack or Heap References +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Most of the operations on mutable references are agnostic to whether +the memory referenced resides on the stack or the heap---the main +difference is that stack references are allocated in a scope and +implicitly reclaimed when they go out of scope; whereas heap +references are explicitly allocated and deallocated. + +The type ``ref t`` is the type of mutable reference that may reside on +either the stack or the heap. We have already seen ``ref t`` used in +the ``incr`` function of the previous section. We show below another +common function to swap the contents of two references: +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //swap$ + :end-before: ``` -Stack references -^^^^^^^^^^^^^^^^ +Reading a reference +................... + + +Let's start by taking a closer look at how dereferencing works in the +function ``value_of`` below: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //value_of$ + :end-before: ``` + +Its slightly more explicit form is shown below, where ``w:erased a`` +is an erased value witnessing the current contents referenced by +``r``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //value_of_explicit$ + :end-before: ``` + +Notice how the precondition requires ``pts_to r w`` while the +postcondition changes to ``pts_to r v``, along with the property that +``v == reveal w``, i.e., the type proves that if we read the reference +the value we get is equal to the logical witness provided. + + +Erased values are for specification and proof only +.................................................. + +The logical witness is an erased value, so one cannot directly use it +in a non-ghost computation. For example, if instead of reading the +reference, we attempt to just return ``reveal w``, the +code fails to check with the error shown below. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //value_of_explicit_fail$ + :end-before: ``` + +.. code-block:: + + Expected a Total computation, but got Ghost + +Writing through a reference +........................... + +The function ``assign`` below shows how to mutate the contents of a +reference---the specification shows that when the function returns, +``r`` points to the assigned value ``v``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //assign$ + :end-before: ``` + + +Dereferencing is explicit +......................... + +Unlike languages like C or Rust which make a distinction between +l-values and r-values and implicitly read the content of references, +in Pulse (like in OCaml), references are explicitly dereferenced. [#]_ +As the program below illustrates, references themselves can be passed +to other functions (e.g., as in/out-parameters) while their current +values must be passed expliclity. + +The function ``add`` takes both a refrence ``r:ref int`` and a value +``n:int`` as arguments: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //add$ + :end-before: ``` + +Meanwhile, the function ``quadruple`` calls ``add`` twice to double +the value stored in ``r`` each time. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //quadruple$ + :end-before: ``` + +The comments show how the proof state evolves after each command. A +few points to note about the Pulse checker: + + * Pulse typechecks each step of a program by checking the current + assumptions in the proof state are sufficient to prove the + precondition of that step, ensuring that all unused permissions + are retained in the context---using the frame rule, discussed in + the previous section + + * Like F*, Pulse tries to instantiate implicit arguments + automatically, e.g., at the second call to ``add``, Pulse + automatically instantiates ``'v`` to ``v2``. + + * Pulse proves ``pure`` properties automatically, by sending queries + to the SMT solver. + + * Pulse also uses the SMT solver to convert ``pts_to r (v2 + v2)`` + to ``pts_to r (4 * 'v)``. + + * Pulse simplifies ``vprops`` implicitly, e.g., Pulse will + automatically rewrite ``emp ** p`` to ``p``. + +Stateful commands are explicitly sequenced +.......................................... + +Pulse expects the results of all stateful operations to be explicitly +``let``-bound. For example, the following code fails to type checked: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //quad FAIL$ + :end-before: ``` + + +.. code-block:: + + - Expected type "int"; but "!r" has type + "stt int + (pts_to r (reveal (*?u93*) _)) + (fun x -> pts_to r x ** pure (reveal (*?u93*) _ == x))" + +The error points to the first occurrence of ``(!r)``. The message is +admittedly cryptic and should be improved. As we'll see in a later +chapter, the type ``stt _ _ _`` is the type of an unevaluated Pulse +comptuation---this error complains that ``add`` expected an argument +of type ``int`` but instead got an unevaluated computation. + +Fractional Permissions +...................... + +Pulse distinguishes read-only references from read/write +references. As in languages like Rust, Pulse ensures that there can be +at most one thread that holds read/write permission to a reference, +although many threads can share read-only references. This ensures +that Pulse programs are free of data races. At a more abstract level, +Pulse's permission system ensures that one can reason locally about +the contents of memory, since if one holds read/write permission to a +reference, one can be sure that its contents cannot be changed by some +part of the program. + +To implement this permission discipline, Pulse uses a system of +fractional permissions, an idea due to `John Boyland +`_. In +particular, the ``pts_to`` predicate that we have been using actually +has an additional implicit arguments that describes how much +permission one holds on a reference. + +The full type of the ``pts_to`` predicate is shown below: + +.. code-block:: fstar + + val pts_to (#a:Type u#0) (r:ref a) (#p:perm) (v:a) : vprop + +We have so far been writing ``pts_to r v`` instead of ``pts_to #a r #p +v``. Usually, one does not need to write the first argument ``#a`` +since it is computed by type inference; the ``#p:perm`` argument is +more interesting---when omitted, it defaults to the value +``full_perm``. The type ``perm`` (defined in +``Steel.FractionalPermission``) is a real number strictly greater than +``0`` and less than or equal to ``1``, where ``1`` is written +``full_perm``. + +The ``pts_to r #full_perm v`` represents exclusive, read/write +permission on a reference. Revisiting the ``assign`` function from +previously, we can write down the permissions explicitly. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //assign_full_perm$ + :end-before: ``` + +In contrast, when reading a reference, any permission ``p`` will do, +as shown below: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //value_of_perm$ + :end-before: ``` + +If we try to write to a reference without holding full permission on +it, Pulse rejects the program, as shown below. -We have already seen the type ``ref t``, as in the ``incr`` function -below: the type ``ref t`` is the type of a memory reference that -typically refers to a memory location on the stack (though, we'll soon -see that it may also be used to refer to memory on the heap). +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: //assign_perm FAIL$ + :end-before: //end assign_perm FAIL$ + +.. code-block:: fstar + + - Cannot prove: + pts_to #a r #full_perm (reveal #a _) + - In the context: + pts_to #a r #p (reveal #a w) + +The full error message requires the F* option ``--print_implicits``. + +The functions ``share`` and ``gather`` allow one to divide and combine +permissions on references, as shown below. + + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //share_ref$ + :end-before: ``` .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //incr + :start-after: ```pulse //gather_ref$ :end-before: ``` + +Stack references +^^^^^^^^^^^^^^^^ + ``let mut`` creates a new stack ref ................................... @@ -89,105 +313,63 @@ the goal to be proven for the postcondition is ``pts_to s 0``. In other words, we no longer have ownership on ``s`` once it goes out of scope. -.. code-block:: +.. code-block:: fstar - Cannot prove: pts_to s 0 - In the context: emp -Dereferencing is explicit -......................... - -Unlike languages like C or Rust which make a distinction between -l-values and r-values and implicitly read the content of references, -in Pulse (like in OCaml), references are explicitly dereferenced. [#]_ -As the program below illustrates, references themselves can be passed -to other functions (e.g., as in/out-parameters) while their current -values must be passed expliclity. - -The function ``add`` takes both a refrence ``r:ref int`` and a value -``n:int`` as arguments: - -.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst - :language: pulse - :start-after: ```pulse //add - :end-before: ``` - -Meanwhile, the function ``four`` allocates a new stack reference ``i`` -initialized to ``1``; calls ``add`` twice to double the value stored -in ``i`` each time. - -.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst - :language: pulse - :start-after: ```pulse //four - :end-before: ``` - -Note, Pulse expects the results of all stateful operations to be -explicitly ``let``-bound. For example, the following code fails to -type checked: - -.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst - :language: pulse - :start-after: ```pulse //four FAIL - :end-before: ``` - -.. code-block:: + +Heap references +^^^^^^^^^^^^^^^ - - Expected type "int"; but "!i" has type - "stt int - (pts_to i (reveal (*?u93*) _)) - (fun x -> pts_to i x ** pure (reveal (*?u93*) _ == x))" +The type ``Pulse.Lib.Box.box t`` is the type of heap references---the +name is meant to evoke Rust's type of heap references, ``Box``. We +use the module alias ``Box`` in what follows: -The error points to the first occurrence of ``(!i)``. The message is -admittedly cryptic and should be improved. As we'll see in a later -chapter, the type ``stt _ _ _`` is the type of an unevaluated Pulse -comptuation---this error complains that ``add`` expected an argument -of type ``int`` but instead got an unevaluated computation. +.. code-block:: fstar -Erased values are for specification and proof only -.................................................. + module Box = Pulse.Lib.Box -Let's take a closer look at how dereferencing works in the function -``value_of`` below: +The ``Box`` module provides most of the same predicates and functions +that we have with regular references, including ``pts_to``, ``(!)``, +``(:=)``, ``share``, and ``gather``. Additionally, heap references are +explicitly allocated using ``alloc`` and deallocated using ``free``, +as shown below. -.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Box.fst :language: pulse - :start-after: ```pulse //value_of$ + :start-after: ```pulse //new_heap_ref$ :end-before: ``` + +Note, we can return a freshly allocated heap reference from a +function, unlike a ``let mut`` scoped, stack-allocated reference. -Its slightly more explicit form is shown below, where ``w:erased a`` -is an erased value witnessing the current contents referenced by -``r``. +In the following example, we use ``open Box;`` to open the namespace +``Box`` in the following scope. -.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Box.fst :language: pulse - :start-after: ```pulse //value_of_explicit$ + :start-after: ```pulse //last_value_of$ :end-before: ``` -Notice how the precondition requires ``pts_to r w`` while the -postcondition changes to ``pts_to r v``, along with the property that -``v == reveal w``, i.e., the type proves that if we read the reference -the value we get is equal to the logical witness provided. +``box t`` references can be demoted to regular ``ref t`` references +for code reuse. For example, in the code below, we increment the +contents of ``r:box int`` by first calling ``Box.to_ref_pts_to`` to +convert ``Box.pts_to r 'v`` to a regular ``pts_to (box_to_ref r) 'v``; +then calling ``incr (box_to_ref r)``; and then converting back to a +``Box.pts_to``. -The logical witness is an erased value, so one cannot directly use it -in a non-ghost computation. For example, if instead of reading the -reference, we attempt to just return ``reveal w``, the -code fails to check with the error shown below. - -.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Box.fst :language: pulse - :start-after: ```pulse //value_of_explicit_fail$ + :start-after: ```pulse //incr_box$ :end-before: ``` -.. code-block:: - - Expected a Total computation, but got Ghost - - -Heap references -^^^^^^^^^^^^^^^ - +Finally, unlike Rust's ``Box`` type, which is always treated +linearly (i.e., in Rust, one always holds exclusive read/write +permission on a ` ``Box``), in Pulse, ``Box.pts_to r #p v`` has an +implicit fractional permission as with regular references. Ghost references ^^^^^^^^^^^^^^^^ From 8ed72247176ccda76397f8436e0adb9be8feb094 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 4 Jan 2024 05:45:44 -0800 Subject: [PATCH 11/65] swap --- book/pulse/pulse_ch2.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index a1bd6d586d1..0ae9f8af3d3 100755 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -29,10 +29,10 @@ difference is that stack references are allocated in a scope and implicitly reclaimed when they go out of scope; whereas heap references are explicitly allocated and deallocated. -The type ``ref t`` is the type of mutable reference that may reside on -either the stack or the heap. We have already seen ``ref t`` used in -the ``incr`` function of the previous section. We show below another -common function to swap the contents of two references: +The type ``Pulse.Lib.Reference.ref t`` is the basic type of a mutable +reference. We have already seen ``ref t`` used in the ``incr`` +function of the previous section. We show below another common +function to swap the contents of two references: .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse From 8fd82aced4d4c784e6df33b930bdab130345b80e Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 4 Jan 2024 12:18:53 -0800 Subject: [PATCH 12/65] existentials, user-defined preds, some control flow --- book/fstar_pygments.py | 6 + book/pulse/pulse.rst | 3 + book/pulse/pulse_ch2.rst | 74 ++++++++- book/pulse/pulse_controlflow.rst | 73 ++++++++ book/pulse/pulse_existentials.rst | 165 +++++++++++++++++++ book/pulse/pulse_user_defined_predicates.rst | 129 +++++++++++++++ 6 files changed, 443 insertions(+), 7 deletions(-) create mode 100755 book/pulse/pulse_controlflow.rst create mode 100755 book/pulse/pulse_existentials.rst create mode 100755 book/pulse/pulse_user_defined_predicates.rst diff --git a/book/fstar_pygments.py b/book/fstar_pygments.py index 0177732c699..63db3f62c6e 100644 --- a/book/fstar_pygments.py +++ b/book/fstar_pygments.py @@ -96,6 +96,12 @@ class CustomLexer(RegexLexer): pulse_keywords = ( "fn", + "fold", + "rewrite", + "each", + "mut", + "ghost", + "atomic" ) class PulseLexer(RegexLexer): diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index a913359dd35..99a2b1023ee 100755 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -78,3 +78,6 @@ dependent types, refinement types, etc. will be of use again. pulse_ch1 pulse_ch2 + pulse_existentials + pulse_user_defined_predicates + pulse_controlflow diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index 0ae9f8af3d3..54312de41d2 100755 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -122,8 +122,61 @@ the value stored in ``r`` each time. :start-after: ```pulse //quadruple$ :end-before: ``` -The comments show how the proof state evolves after each command. A -few points to note about the Pulse checker: +Inspecting the proof state +.......................... + +A Pulse program is checked one stateful operation at a time, "pushing +through" the ``vprop`` assertions starting with the precondition, +until the end of function's body. The inferred ``vprop`` at the exit +of a function must match the annotated postcondition. Along the way, +the Pulse checker will make several calls to the SMT solver to prove +that, say, ``pts_to x (v + v)`` is equal to ``pts_to x (2 * v)``. + +At each point in the program, the Pulse checker maintains a proof +state, which has two components: + + * A typing environment, binding variables in scope to their types, + including some refinement types that reflect properties about + those variables in scope, e.g., ``x:int; y:erased int; _:squash (x == reveal y)``. + + * A separation logic context, called just "the context", or + sometimes "the ``vprop`` context". The context contains all known + facts about the current state of the program. + +Pulse provides a command called ``show_proof_state`` that allows the +user to inspect the proof state at a particular program point, +aborting the Pulse checker at that point. It's quite common when +developing a Pulse program to repeatedly inspect the proof state and +to advance it by a single or just a few steps at a time. This makes +the experience of developing a Pulse program quite interactive, +similar perhaps to writing tactics in F* or other languages. Except, +in Pulse, one incrementally writes an imperative program together with +its proof of correctness. + +Here below is the ``quadruple`` program again, with the proof states +annotated at each point, and a ``show_proof_state`` command in the +middle. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //quadruple_show_proof_state$ + :end-before: ``` + +The output from ``show_proof_state`` is shown below: + +.. code-block:: pulse + + - Current context: + pts_to r (reveal (hide v1) + v1) ** + emp + - In typing environment: + [_#5 : unit, + _#4 : squash (reveal 'v == v1), + v1#3 : int, + 'v#2 : erased int, + r#1 : ref int] + +The comments show how the proof state evolves after each command. * Pulse typechecks each step of a program by checking the current assumptions in the proof state are sufficient to prove the @@ -135,20 +188,21 @@ few points to note about the Pulse checker: automatically, e.g., at the second call to ``add``, Pulse automatically instantiates ``'v`` to ``v2``. - * Pulse proves ``pure`` properties automatically, by sending queries - to the SMT solver. + * Pulse automatically moves any ``pure p`` property in the ``vprop`` + context to a ``squash p`` hypothesis in the typing + environment. Pulse also proves ``pure`` properties automatically, + by sending queries to the SMT solver, which can make use of the + hypothesis in the typing environment only. * Pulse also uses the SMT solver to convert ``pts_to r (v2 + v2)`` to ``pts_to r (4 * 'v)``. - * Pulse simplifies ``vprops`` implicitly, e.g., Pulse will - automatically rewrite ``emp ** p`` to ``p``. Stateful commands are explicitly sequenced .......................................... Pulse expects the results of all stateful operations to be explicitly -``let``-bound. For example, the following code fails to type checked: +``let``-bound. For example, the following code fails to type check: .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse @@ -252,6 +306,12 @@ permissions on references, as shown below. :start-after: ```pulse //gather_ref$ :end-before: ``` +The type of ``gather_ref`` has an additional interesting element: its +postcondition proves that ``'v0 == 'v1``. That is, since ``x`` can +point to at most one value, given two separate points-to assertions +about ``x``, allows one to conclude that the pointed-to witnesses are +identical. + Stack references ^^^^^^^^^^^^^^^^ diff --git a/book/pulse/pulse_controlflow.rst b/book/pulse/pulse_controlflow.rst new file mode 100755 index 00000000000..7fb9a0ba6d4 --- /dev/null +++ b/book/pulse/pulse_controlflow.rst @@ -0,0 +1,73 @@ +.. _Pulse_ControlFlow: + +Conditionals, Loops, and Recursion +================================== + +To start writing interesting programs, we need a few control +constructs. In this chapter, we'll write our first while loops, +conditionals, and recursive programs. + + +Conditionals +............ + +Here's a simple program that returns the maximum value stored in two +references. + +.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst + :language: pulse + :start-after: //SNIPPET_START: max$ + :end-before: //SNIPPET_END: max$ + +This program illustrates a very common specification style. + + * We have a pure, F* function ``max_spec`` + + * And a Pulse function working on mutable references, with a + specification that relates it to the pure F* spec. In this case, + we prove that ``max`` behaves like ``max_spec`` on the logical + values that witness the contents of the two references. + +The implementation of ``max`` uses a Pulse conditional statement. Its +syntax is different from the F* ``if-then-else`` expression: Pulse +uses a more imperative syntax with curly braces, which should be +familiar from languages like C. + +Limitation: Non-tail Conditionals ++++++++++++++++++++++++++++++++++ + +Pulse's inference machinery does not yet support conditionals that +appear in non-tail position. For example, this variant of ``max`` +fails, with the error message shown below. + +.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst + :language: pulse + :start-after: ```pulse //max_alt_fail$ + :end-before: ``` + +.. code-block:: + + Pulse cannot yet infer a postcondition for a non-tail conditional statement; + Either annotate this `if` with `returns` clause; or rewrite your code to use a tail conditional + +Here's an annotated version of ``max_alt`` that succeeds. + +.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst + :language: pulse + :start-after: ```pulse //max_alt$ + :end-before: ``` + +We are working on adding inference for non-tail conditionals. + +Pattern matching +................ + + + +While loops +........... + + + +Recursion +......... diff --git a/book/pulse/pulse_existentials.rst b/book/pulse/pulse_existentials.rst new file mode 100755 index 00000000000..3df7b967f0e --- /dev/null +++ b/book/pulse/pulse_existentials.rst @@ -0,0 +1,165 @@ +.. _Pulse_Existentials: + +Existential Quantification +========================== + +A very common specification style in Pulse involves the use of the +existential quantifier. Before we can start to write interesting +examples, let's take a brief look at how existential quantification +works. + +As mentioned in the :ref:`introduction to Pulse `, one +of the connectives of Pulse's separation logic is the existential +quantifier. Its syntax is similar to F*'s existential quantifier, +except it is written ``exists*`` instead of just ``exists``, and its +body is a ``vprop``, as in the examples shown below. + +.. code-block:: pulse + + exists* (v:nat). pts_to x v + + exists* v. pts_to x v + + exists* v1 v2. pts_to x v1 ** pts_to y v2 + + ... + + +Some simple examples +.................... + +Looking back to the ``assign`` example from the previous chapter +(shown below), you may have wondered why we bothered to bind a logical +variable ``'v`` in precondition of the specification, since it is +never actually used in any other predicate. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: ```pulse //assign$ + :end-before: ``` + +Another way to write the specification of ``assign`` is shown below. + +.. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst + :language: pulse + :start-after: ```pulse //assign$ + :end-before: ``` + +This time, in the precondition, we use an existential quantifier to +say that ``assign`` is callable in a context where ``x`` points to any +value ``w``. + +Usually, however, the postcondition of a function *relates* the +initial state prior to the call to the state after the call and +existential variables are only in scope as far to the right as +possible of the enclosing ``vprop``. So, existential quantifiers in +the precondition of a function are not so common. + +To illustrate, the following attempted specification of ``incr`` does +not work, since the existentially bound ``w0`` is not in scope for the +postcondition. + +.. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst + :language: pulse + :start-after: //SNIPPET_START: incr_fail$ + :end-before: //SNIPPET_END: incr_fail$ + +However, existential quantification often appears in postconditions, +e.g., in order to abstract the behavior of function by underspecifying +it. To illustrate, consider the function ``make_even`` below. It's +type states that it sets the contents of ``x`` to some even number +``w1``, without specifying ``w1`` exactly. It also uses an existential +quantification in its precondition, since its postcondition does not +depend on the initial value of ``x``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst + :language: pulse + :start-after: ```pulse //make_even$ + :end-before: ``` + +Manipulating existentials +......................... + +In a previous chapter on :ref:`handling classical connectives +`, we saw how F* provides various constructs for +introducing and eliminating logical connectives, including the +existential quantifier. Pulse also provides constructs for working +explicitly with existential quantifiers, though, usually, Pulse +automation takes care of introducing and eliminating existentials +behind the scenes. However, the explicit operations are sometimes +useful, and we show a first example of how they work below + +.. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst + :language: pulse + :start-after: ```pulse //make_even_explicit$ + :end-before: ``` + +Eliminating existentials +++++++++++++++++++++++++ + +The form ``with w0...wn. assert p; rest`` is often used as an +eliminator for an existential. When the context contains ``exists* +x0...xn. p``, the ``with`` construct binds ``w0 ... wn`` to the +existentially bound variables in the remainder of the scope ``rest``. + +A ``show_proof_state`` immediately after the ``with w0. assert (pts_to +x w0)`` prints the following: + +.. code-block:: pulse + + - Current context: + pts_to x (reveal w0) ** + emp + - In typing environment: + [w0#2 : erased int, + x#1 : ref int] + +That is, we have ``w0:erased int`` in scope, and ``pts_to x (reveal +w0)`` in context. + +Here is another example usage of ``with``, this time with multiple +binders. + +.. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst + :language: pulse + :start-after: ```pulse //make_even_explicit_alt$ + :end-before: ``` + +When there is a single existential formula in the context, one can +write ``with x1..xn. _`` to "open" the formula, binding its witnesses +in scope. A ``show_proof_state`` after the first line prints: + +.. code-block:: pulse + + - Current context: + pts_to x (reveal wx) ** + pts_to y (reveal wy) ** + pure (eq2 (op_Modulus (reveal wx) 2) (op_Modulus (reveal wy) 2)) ** + emp + - In typing environment: + [_#5 : squash (eq2 (op_Modulus (reveal wx) 2) (op_Modulus (reveal wy) 2)), + wy#4 : erased int, + wx#3 : erased int, + y#2 : ref int, + x#1 : ref int] + + +Introducing existentials +++++++++++++++++++++++++ + +The Pulse checker will automatically introduce existential formulas by +introduces new unification variables for each existentially bound +variable, and then trying to find solutions for those variables by +matching ``vprops`` in the goal with those in the context. + +However, one can also introduce existential formulas explicitly, using +the ``introduce exists*`` syntax, as seen in the two examples +above. In general, one can write + +.. code-block:: pulse + + introduce exists* x1 .. xn. p + with w1...wn + +explicitly providing witnesses ``w1..wn`` for each of the +existentially bound variables ``x1..xn``. diff --git a/book/pulse/pulse_user_defined_predicates.rst b/book/pulse/pulse_user_defined_predicates.rst new file mode 100755 index 00000000000..4daba381a1b --- /dev/null +++ b/book/pulse/pulse_user_defined_predicates.rst @@ -0,0 +1,129 @@ +.. _Pulse_DefinedVProps: + +User-defined Predicates +======================= + +In addition to the vprop predicates and connectives that the Pulse +libraries provide, users very commonly define their own ``vprops``. We +show a few simple examples here---subsequent examples will make heavy +use of user-defined predicates. + + +Diagonal Pairs +.............. + +A simple example of a user-defined abstraction is show below. + +.. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst + :language: pulse + :start-after: //SNIPPET_START: pts_to_diag$ + :end-before: //SNIPPET_END: pts_to_diag$ + +``pts_to_diag r v`` is a ``vprop`` defined in F* representing a +reference to a pair whose components are equal. + +We can use this abstraction in a Pulse program, though we have to be +explicit about folding and unfolding the predicate. + +.. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst + :language: pulse + :start-after: ```pulse //double$ + :end-before: ``` + +The ``unfold p`` command checks that ``p`` is provable in the current +context by some term ``q``, and then rewrites the context by replacing +that occurrence of ``q`` with the term that results from unfolding the +head symbol of ``p``. A ``show_proof_state`` after the ``unfold`` +shows that we have a ``pts_to r (reveal 'v, reveal 'v)`` in the +context, exposing the abstraction of the ``pts_to_diag`` predicate. + +At the end of function, we use the ``fold p`` command: this checks +that the unfolding of ``p`` is provable in the context by some term +``q`` and then replaces ``q`` in the context with ``p``. + +``fold`` and ``unfold`` is currently very manual in Pulse. While in +the general case, including with recursively defined predicates, +automating the placement of folds and unfolds is challenging, many +common cases (such as the ones here) can be easily automated. We are +currently investigating adding support for this. + +Some initial support for this is already available, inasmuch as Pulse +can sometimes figure out the arguments to the vprops that need to be +folded/unfolded. For instance, in the code below, we just mention the +name of the predicate to be unfolded/folded, without needing to +provide all the arguments. + +.. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst + :language: pulse + :start-after: ```pulse //double_alt$ + :end-before: ``` + +Mutable Points +.............. + +As a second, perhaps more realistic example of a user-defined +abstraction, we look at defining a simple mutable data structure: a +structure with two mutable integer fields, representing a +2-dimensional point. + +.. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst + :language: fstar + :start-after: //SNIPPET_START: point$ + :end-before: //SNIPPET_END: point$ + +A ``point`` is just an F* record containing two +references. Additionally, we define ``is_point``, a ``vprop``, +sometimes called a "representation predicate", for a +``point``. ``is_point p xy`` says that ``p`` is a representation of +the logical point ``xy``, where ``xy`` is pure, mathematical pair. + +We can define a function ``move``, which translates a point by some +offset ``dx, dy``. + +.. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst + :language: pulse + :start-after: ```pulse //move$ + :end-before: ``` + +Implementing ``move`` is straightforward, but like before, we have to +``unfold`` the ``is_point`` predicate first, and then fold it back up +before returning. + +Unfortunately, Pulse cannot infer the instantiation of ``is_point`` +when folding it. A ``show_proof_state`` prior to the fold should help +us see why: + + * We have ``pts_to p.x (x + dx) ** pts_to p.y (y + dy)`` + + * For ``fold (is_point p.x ?w)`` to succeed, we rely on F*'s type + inference to find a solution for the unsolved witness ``?w`` such + that ``fst ?w == (x + dx)`` and ``snd ?w == (y + dy)``. This + requires an eta-expansion rule for pairs to solve ``?w := (x + dx, + y + dy)``, but F*'s type inference does not support such a rule + for pairs. + +So, sadly, we have to provide the full instantiation ``is_point p (x + +dx, y + dy)`` to complete the proof. + +This pattern is a common problem when working with representation +predicates that are indexed by complex values, e.g., pairs or +records. It's common enough that it is usually more convenient to +define a helper function to fold the predicate, as shown below. + +.. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst + :language: pulse + :start-after: ```pulse //fold_is_point$ + :end-before: ``` + +.. note:: + + We've marked this helper function ``ghost``. We'll look into + ``ghost`` functions in much more detail in a later chapter. + +This allows type inference to work better, as shown below. + +.. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst + :language: pulse + :start-after: ```pulse //move_alt$ + :end-before: ``` + From a71bb876b25d28fd0265fe4eb26b08df86c53789 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 4 Jan 2024 12:19:17 -0800 Subject: [PATCH 13/65] perm --- book/pulse/pulse.rst | 0 book/pulse/pulse_ch1.rst | 0 book/pulse/pulse_ch2.rst | 0 book/pulse/pulse_controlflow.rst | 0 book/pulse/pulse_existentials.rst | 0 book/pulse/pulse_user_defined_predicates.rst | 0 6 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 book/pulse/pulse.rst mode change 100755 => 100644 book/pulse/pulse_ch1.rst mode change 100755 => 100644 book/pulse/pulse_ch2.rst mode change 100755 => 100644 book/pulse/pulse_controlflow.rst mode change 100755 => 100644 book/pulse/pulse_existentials.rst mode change 100755 => 100644 book/pulse/pulse_user_defined_predicates.rst diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst old mode 100755 new mode 100644 diff --git a/book/pulse/pulse_ch1.rst b/book/pulse/pulse_ch1.rst old mode 100755 new mode 100644 diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst old mode 100755 new mode 100644 diff --git a/book/pulse/pulse_controlflow.rst b/book/pulse/pulse_controlflow.rst old mode 100755 new mode 100644 diff --git a/book/pulse/pulse_existentials.rst b/book/pulse/pulse_existentials.rst old mode 100755 new mode 100644 diff --git a/book/pulse/pulse_user_defined_predicates.rst b/book/pulse/pulse_user_defined_predicates.rst old mode 100755 new mode 100644 From 2d005803a12a178186dc662e5691ecdf4631ee48 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 4 Jan 2024 12:58:36 -0800 Subject: [PATCH 14/65] rewriting etc. --- book/fstar_pygments.py | 3 +- book/pulse/pulse_ch2.rst | 20 +++-- book/pulse/pulse_user_defined_predicates.rst | 93 ++++++++++++++++++++ 3 files changed, 110 insertions(+), 6 deletions(-) diff --git a/book/fstar_pygments.py b/book/fstar_pygments.py index 63db3f62c6e..daf6d19a55e 100644 --- a/book/fstar_pygments.py +++ b/book/fstar_pygments.py @@ -101,7 +101,8 @@ class CustomLexer(RegexLexer): "each", "mut", "ghost", - "atomic" + "atomic", + "show_proof_state" ) class PulseLexer(RegexLexer): diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index 54312de41d2..a1aa861dbf0 100644 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -182,7 +182,9 @@ The comments show how the proof state evolves after each command. assumptions in the proof state are sufficient to prove the precondition of that step, ensuring that all unused permissions are retained in the context---using the frame rule, discussed in - the previous section + the previous section. Given a context that is equivalent to ``p ** + q``, if ``p`` is sufficient to prove ``goal``, then ``p`` is + called *the support* for ``goal``, while ``q`` is the *frame*. * Like F*, Pulse tries to instantiate implicit arguments automatically, e.g., at the second call to ``add``, Pulse @@ -196,7 +198,7 @@ The comments show how the proof state evolves after each command. * Pulse also uses the SMT solver to convert ``pts_to r (v2 + v2)`` to ``pts_to r (4 * 'v)``. - + Stateful commands are explicitly sequenced .......................................... @@ -340,9 +342,17 @@ are true after each command. * After calling ``incr i``, we have ``pts_to i (0 + 1)`` * Finally, we dereference ``i`` using ``!i`` and return ``v:int`` - the current value of ``i`` at which point the scope of ``i`` ends - and the memory it points to is reclaimed. The final assertion - corresponds to the postcondition of ``one``. + the current value of ``i``. + + * At the point where the scope of a ``let mut x`` ends, the Pulse + checker requires that the context contains ``pts_to x #full_perm + _v`` for some value ``_v``. This ensures that the code cannot + squirrel away a permission to the soon-to-be out-of-scope + reference in some other permission. Once the scope ends, and the + memory it points to is reclaimed, and the ``pts_to x #full_perm + _v`` is consumed. + + A few additional points to note here: diff --git a/book/pulse/pulse_user_defined_predicates.rst b/book/pulse/pulse_user_defined_predicates.rst index 4daba381a1b..1b929b01b9d 100644 --- a/book/pulse/pulse_user_defined_predicates.rst +++ b/book/pulse/pulse_user_defined_predicates.rst @@ -127,3 +127,96 @@ This allows type inference to work better, as shown below. :start-after: ```pulse //move_alt$ :end-before: ``` +Rewriting +......... + +In addition to ``fold`` and ``unfold``, one also often uses the +``rewrite`` command when working with defined predicates. Its general +form is: + +.. code-block:: + + with x1 ... xn. rewrite p as q; + rest + +Its behavior is to find a substitution ``subst`` that instantiates the +``x1 ... xn`` as ``v1 ... vn``, such that ``subst(p)`` is supported by +``c`` in the context, Pulse aims to prove that ``subst(p) == +subst(q)`` and replaces ``c`` in the context by ``subst(q)`` and +proceeds to check ``subst(rest)``. + +To illustrate this at work, consider the program below: + +.. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst + :language: pulse + :start-after: ```pulse //create_and_move$ + :end-before: ``` + +We allocate two references and put them in the structure ``p``. Now, +to call ``fold_is_point``, we need ``pts_to p.x _`` and ``pts_to p.y +_``, but the context only contains ``pts_to x _`` and ``pts_to y +_``. The ``rewrite`` command transforms the context as needed. + +At the end of the function, we need to prove that ``pts_to x _`` and +``pts_to y _`` as we exit the scope of ``y`` and ``x``, so that they +can be reclaimed. Using ``rewrite`` in the other direction +accomplishes this. + +This is quite verbose. As with ``fold`` and ``unfold``, fully +automated ``rewrite`` in the general case is hard, but many common +cases are easy and we expect to add support for that to the Pulse +checker. + +In the meantime, Pulse provides a shorthand to make some common +rewrites easier. + +The ``rewrite each`` command has the most general form: + +.. code-block:: pulse + + with x1 ... xn. rewrite each e1 as e1', ..., en as en' in goal + +This is equivalent to: + +.. code-block:: pulse + + with x1 ... xn. assert goal; + rewrite each e1 as e1', ..., en as en' in goal + +.. code-block:: pulse + + rewrite each e1 as e1', ..., en as en' in goal + +is equivalent to + +.. code-block:: pulse + + rewrite goal as goal' + +where ``goal'`` is computed by rewriting, in parallel, every +occurrence of ``ei`` as ``ei'`` in ``goal``. + +Finally, one can also write: + +.. code-block:: pulse + + rewrite each e1 as e1', ..., en as en' + +omitting the ``goal`` term. In this case, the ``goal`` is taken to be +the entire current ``vprop`` context. + +Using ``rewrite each ...`` makes the code somewhat shorter: + +.. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst + :language: pulse + :start-after: ```pulse //create_and_move_alt$ + :end-before: ``` + + + + + + + + + From 083a22211e7b27c7c17842a718dd92f259062e9a Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 4 Jan 2024 15:04:12 -0800 Subject: [PATCH 15/65] more control flow --- book/pulse/pulse_controlflow.rst | 166 ++++++++++++++++++- book/pulse/pulse_user_defined_predicates.rst | 4 +- 2 files changed, 166 insertions(+), 4 deletions(-) diff --git a/book/pulse/pulse_controlflow.rst b/book/pulse/pulse_controlflow.rst index 7fb9a0ba6d4..fcc78e5451e 100644 --- a/book/pulse/pulse_controlflow.rst +++ b/book/pulse/pulse_controlflow.rst @@ -59,9 +59,171 @@ Here's an annotated version of ``max_alt`` that succeeds. We are working on adding inference for non-tail conditionals. -Pattern matching -................ +Pattern matching with nullable references +......................................... +To illustrate the use of pattern matching, consider the following +representation of a possibly null reference. + +.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst + :language: fstar + :start-after: //SNIPPET_START: nullable_ref$ + :end-before: //SNIPPET_END: nullable_ref$ + +Representation predicate +++++++++++++++++++++++++ + +We can represent a nullable ref as just an ``option (ref a)`` coupled +with a representation predicate, ``pts_to_or_null``. A few points to +note: + + * The notation ``(#[default_arg (\`full_perm)] p:perm)`` is F* + syntax for an implicit argument which when omitted defaults to + ``full_perm``---this is exactly how predicates like + ``Pulse.Lib.Reference.pts_to`` are defined. + + * The definition is by cases: if the reference ``x`` is ``None``, + then the logical witness is ``None`` too. + + * Otherwise, the underlying reference points to some value ``w`` and + the logical witness ``v == Some w`` agrees with that value. + +Note, one might consider defining it this way: + +.. code-block:: fstar + + let pts_to_or_null #a + (x:nullable_ref a) + (#[default_arg (`full_perm)] p:perm) + (v:option a) + : vprop + = match x with + | None -> pure (v == None) + | Some x -> pure (Some? v) ** pts_to x #p (Some?.v v) + +However, unlike F*'s conjunction ``p /\ q`` where the well-typedness +of ``q`` can rely on ``p``, the ``**`` operator is not left-biased; so +``(Some?.v v)`` cannot be proven in this context and the definition is +rejected. + +Another style might be as follows: + +.. code-block:: fstar + + let pts_to_or_null #a + (x:nullable_ref a) + (#[default_arg (`full_perm)] p:perm) + (v:option a) + : vprop + = match x, v with + | None, None -> emp + | Some x, Some w -> pts_to x #p w + | _ -> pure False + +This could also work, though it would require handling an additional +(impossible) case. + +Reading a nullable ref +++++++++++++++++++++++ + +Let's try our first pattern match in Pulse: + +.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst + :language: pulse + :start-after: ```pulse //read_nullable$ + :end-before: ``` + +The syntax of pattern matching in Pulse is more imperative and +Rust-like than what F* uses. + + * The entire body of match is enclosed within braces + + * Each branch is also enclosed within braces. + + * Pulse only supports simple patterns with a single top-level + constructor applied to variables, or variable patterns: e.g., you + cannot write ``Some (Some x)`` as a pattern. + +The type of ``read_nullable`` promises to return a value equal to the +logical witness of its representation predicate. + +The code is a little tedious---we'll see how to clean it up a bit +shortly. + +A ``show_proof_state`` in the ``Some x`` branch prints the following: + +.. code-block:: + + - Current context: + pts_to_or_null r (reveal 'v) + - In typing environment: + [branch equality#684 : squash (eq2 r (Some x)), + ... + +The interesting part is the ``branch equality`` hypothesis, meaning +that in this branch, we can assume that ``(r == Some x)``. So, the +first thing we do is to rewrite ``r``; then we ``unfold`` the +representation predicate; read the value ``o`` out of ``x``; fold the +predicate back; rewrite in the other direction; and return ``Some +o``. The ``None`` case is similar. + +Another difference between Pulse and F* matches is that Pulse does not +provide any negated path conditions. For example, in the example +below, the assertion fails, since the pattern is only a wildcard and +the Pulse checker does not prove ``not (Some? x)`` as the path +condition hypothesis for the preceding branches not taken. + +.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst + :language: pulse + :start-after: //SNIPPET_START: read_nullable_alt_fail$ + :end-before: //SNIPPET_END: read_nullable_alt_fail$ + +We plan to enhance the Pulse checker to also provide these negated +path conditions. + + +Helpers ++++++++ + +When a ``vprop`` is defined by cases (like ``pts_to_or_null``) it is +very common to have to reason according to those cases when pattern +matching. Instead of rewriting, unfolding, folding, and rewriting +every time, one can define helper functions to handle these cases. + + +.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst + :language: pulse + :start-after: //SNIPPET_START: pts_to_or_null_helpers$ + :end-before: //SNIPPET_END: pts_to_or_null_helpers$ + +These functions are all marked ``ghost``, indicating that they are +purely for proof purposes only. + +Writing these helpers is often quite mechanical: One could imagine +that the Pulse checker could automatically generate them from the +definition of ``pts_to_or_null``. Using F*'s metaprogramming support, +a user could also auto-generate them in a custom way. For now, we +write them by hand. + +Using the helpers, case analyzing a nullable reference is somewhat +easier: + +.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst + :language: pulse + :start-after: ```pulse //read_nullable_alt$ + :end-before: ``` + + +Writing a nullable reference +++++++++++++++++++++++++++++ + +Having defined our helpers, we can use them repeatedly. For example, +here is a function to write a nullable reference. + +.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst + :language: pulse + :start-after: ```pulse //write_nullable$ + :end-before: ``` While loops diff --git a/book/pulse/pulse_user_defined_predicates.rst b/book/pulse/pulse_user_defined_predicates.rst index 1b929b01b9d..6fa1cfd99f4 100644 --- a/book/pulse/pulse_user_defined_predicates.rst +++ b/book/pulse/pulse_user_defined_predicates.rst @@ -9,8 +9,8 @@ show a few simple examples here---subsequent examples will make heavy use of user-defined predicates. -Diagonal Pairs -.............. +Fold and Unfold with Diagonal Pairs +................................... A simple example of a user-defined abstraction is show below. From e4d84ad5dd4863948cd2f524647d99c9bcdb42de Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 4 Jan 2024 21:19:40 -0800 Subject: [PATCH 16/65] loops --- book/fstar_pygments.py | 4 +- book/pulse/pulse.rst | 4 +- ...controlflow.rst => pulse_conditionals.rst} | 38 ++-- book/pulse/pulse_loops.rst | 178 ++++++++++++++++++ 4 files changed, 200 insertions(+), 24 deletions(-) rename book/pulse/{pulse_controlflow.rst => pulse_conditionals.rst} (86%) create mode 100755 book/pulse/pulse_loops.rst diff --git a/book/fstar_pygments.py b/book/fstar_pygments.py index daf6d19a55e..236c1215ce6 100644 --- a/book/fstar_pygments.py +++ b/book/fstar_pygments.py @@ -102,7 +102,9 @@ class CustomLexer(RegexLexer): "mut", "ghost", "atomic", - "show_proof_state" + "show_proof_state", + "while", + "invariant" ) class PulseLexer(RegexLexer): diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 99a2b1023ee..65f00b288e3 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -80,4 +80,6 @@ dependent types, refinement types, etc. will be of use again. pulse_ch2 pulse_existentials pulse_user_defined_predicates - pulse_controlflow + pulse_conditionals + pulse_loops + diff --git a/book/pulse/pulse_controlflow.rst b/book/pulse/pulse_conditionals.rst similarity index 86% rename from book/pulse/pulse_controlflow.rst rename to book/pulse/pulse_conditionals.rst index fcc78e5451e..d314f531af5 100644 --- a/book/pulse/pulse_controlflow.rst +++ b/book/pulse/pulse_conditionals.rst @@ -1,20 +1,20 @@ -.. _Pulse_ControlFlow: +.. _Pulse_Conditionals: -Conditionals, Loops, and Recursion -================================== +Conditionals +============ To start writing interesting programs, we need a few control -constructs. In this chapter, we'll write our first while loops, -conditionals, and recursive programs. +constructs. In this chapter, we'll write some programs with branches +of two kinds: ``if/else`` and ``match``. -Conditionals -............ +A Simple Branching Program: Max +............................... Here's a simple program that returns the maximum value stored in two references. -.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse :start-after: //SNIPPET_START: max$ :end-before: //SNIPPET_END: max$ @@ -40,7 +40,7 @@ Pulse's inference machinery does not yet support conditionals that appear in non-tail position. For example, this variant of ``max`` fails, with the error message shown below. -.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse :start-after: ```pulse //max_alt_fail$ :end-before: ``` @@ -52,7 +52,7 @@ fails, with the error message shown below. Here's an annotated version of ``max_alt`` that succeeds. -.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse :start-after: ```pulse //max_alt$ :end-before: ``` @@ -65,7 +65,7 @@ Pattern matching with nullable references To illustrate the use of pattern matching, consider the following representation of a possibly null reference. -.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: fstar :start-after: //SNIPPET_START: nullable_ref$ :end-before: //SNIPPET_END: nullable_ref$ @@ -128,7 +128,7 @@ Reading a nullable ref Let's try our first pattern match in Pulse: -.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse :start-after: ```pulse //read_nullable$ :end-before: ``` @@ -173,7 +173,7 @@ below, the assertion fails, since the pattern is only a wildcard and the Pulse checker does not prove ``not (Some? x)`` as the path condition hypothesis for the preceding branches not taken. -.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse :start-after: //SNIPPET_START: read_nullable_alt_fail$ :end-before: //SNIPPET_END: read_nullable_alt_fail$ @@ -191,7 +191,7 @@ matching. Instead of rewriting, unfolding, folding, and rewriting every time, one can define helper functions to handle these cases. -.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse :start-after: //SNIPPET_START: pts_to_or_null_helpers$ :end-before: //SNIPPET_END: pts_to_or_null_helpers$ @@ -208,7 +208,7 @@ write them by hand. Using the helpers, case analyzing a nullable reference is somewhat easier: -.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse :start-after: ```pulse //read_nullable_alt$ :end-before: ``` @@ -220,16 +220,10 @@ Writing a nullable reference Having defined our helpers, we can use them repeatedly. For example, here is a function to write a nullable reference. -.. literalinclude:: ../code/pulse/PulseTutorial.ControlFlow.fst +.. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse :start-after: ```pulse //write_nullable$ :end-before: ``` -While loops -........... - - -Recursion -......... diff --git a/book/pulse/pulse_loops.rst b/book/pulse/pulse_loops.rst new file mode 100755 index 00000000000..108722546c3 --- /dev/null +++ b/book/pulse/pulse_loops.rst @@ -0,0 +1,178 @@ +Loops +###### + +In this chapter, we'll see how various looping constructs work in +Pulse, starting with ``while``. + + +While loops: General form +......................... + +The form of a while loop is: + +.. code-block:: pulse + + while ( guard ) + invariant (b:bool). p + { body } + +Where + + * ``guard`` is a Pulse program that returns a ``b:bool`` + + * ``body`` is a Pulse program that returns ``unit`` + + * ``invariant (b:bool). p`` is an invariant where + + - ``exists* b. p`` must be provable before the loop is entered and as a postcondition of ``body``. + + - ``exists* b. p`` is the precondition of the guard, and ``p b`` + is its postcondition, i.e., the ``guard`` must satisfy: + + .. code-block:: pulse + + requires exists* b. p + returns b:bool + ensures p + + - the postcondition of the entire loop is ``invariant false``. + +Countdown +......... + +Here's our first Pulse program with a loop: ``count_down`` repeatedly +decrements a reference until it reaches ``0``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: pulse + :start-after: ```pulse //count_down$ + :end-before: ``` + +One way to understand the invariant is that it describes program +assertions at three different program points. + + * When ``b==true``, the invariant describes the program state at the + start of the loop body; + + * when ``b==false``, the invariant describes the state at the end of + the loop; + + * when ``b`` is undetermined, the invariant describes the property + of the program state just before the guard is (re)executed, i.e., + at the entry to the loop and at end of loop body. + +Coming up with an invariant to describe a loop often requires some +careful thinking. This one is relatively easy, hopefully intuitive. + +While loops in Pulse are perhaps a bit more general than in other +languages. The ``guard`` is an arbitrary Pulse program, not just a +program that reads some local variables. For example, here's another +version of ``count_down`` where the ``guard`` does all the work and +the loop body is empty, and we don't need an auxiliary ``keep_going`` +variable. + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: pulse + :start-after: ```pulse //count_down2$ + :end-before: ``` + + +Partial correctness +................... + +By default, Pulse's logic is designed for partial correctness. This +means that programs are allowed to loop forever. When we say that +program returns ``v:t`` satisfying a postcondition ``p``, this should +be understood to mean that the program could loop forever, but if it +does return, it is guaranteed to return a ``v:t`` where the state +satisfies ``p v``. + +This partial correctness interpretation means that the following +infinitely looping variant of our program is also accepted: + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: pulse + :start-after: ```pulse //count_down_loopy$ + :end-before: ``` + +This program increments instead of decrement ``x``, but it still +satisfies the same invariant as before, since it always loops forever. + +We do have a fragment of the Pulse logic, notably the logic of +``ghost`` and ``atomic`` computations that is guranteed to always +terminate. We plan to also support a version of the Pulse logic for +general purpose sequential programs (i.e., no concurrency) that is +also terminating. + +Multiply by repeated addition +............................. + +Our next program with a loop multiplies two natural numbers ``x, y`` +by repeatedly adding ``y`` to an accumulator ``x`` times. This +program has a bit of history: A 1949 paper by Alan Turing titled +`"Checking a Large Routine" +`_ is +often cited as the first paper about proving the correctness of a +computer program. The program that Turing describes is one that +implements multiplication by repeated addition. + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: pulse + :start-after: ```pulse //multiply_by_repeated_addition$ + :end-before: ``` + +A few noteworthy points: + + * Both the counter ``ctr`` and the accumulator ``acc`` are declared + ``nat``, which implicitly, by refinement typing, provides an + invariant that they are both always at least ``0``. This + illustrates how Pulse provides a separation logic on top of F*'s + existing dependent type system. + + * The invariant says that the counter never exceeds ``x``; the + accumulator is always the product of counter and ``y``; and the + loop continues so long as the counter is strictly less than ``x``. + +Summing the first ``N`` numbers +............................... + +This next example shows a Pulse program that sums the first ``n`` +natural numbers. It illustrates how Pulse programs can developed along +with pure F* specifications and lemmas. + +We start with a specification of ``sum``, a simple recursive function +in F* along with a lemma that proves the well-known identity about +this sum. + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: fstar + :start-after: //SNIPPET_START: sum$ + :end-before: //SNIPPET_END: sum$ + +Now, let's say we want to implement ``isum``, an iterative version of +``sum``, and prove that it satisfies the identity proven by +``sum_lemma``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: pulse + :start-after: //SNIPPET_START: isum$ + :end-before: //SNIPPET_END: isum$ + +This program is quite similar to ``multiply_by_repeated_addition``, +but with a couple of differences: + + * The invariant says that the current value of the accumulator holds + the sum of the the first ``c`` numbers, i.e., we prove that the + loop refines the recursive implementation of ``sum``, without + relying on any properties of non-linear arithmetic---notice, we + have disabled non-linear arithmetic in Z3 with a pragma. + + * Finally, to prove the identity we're after, we just call the F* + ``sum_lemma`` that has already been proven from within Pulse, and + the proof is concluded. + +The program is a bit artificial, but hopefully it illustrates how +Pulse programs can be shown to first F* specificational program, and +then to rely on mathematical reasoning on those specificational +programs to conclude properties about the Pulse program itself. + From 88ac8871a287cd426adf110f1442e7e0fe238401 Mon Sep 17 00:00:00 2001 From: Aseem Rastogi Date: Fri, 5 Jan 2024 11:49:06 +0000 Subject: [PATCH 17/65] chapter on mutable arrays --- book/Makefile | 2 +- book/pulse/pulse.rst | 2 +- book/pulse/pulse_arrays.rst | 192 ++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 book/pulse/pulse_arrays.rst diff --git a/book/Makefile b/book/Makefile index c4a0c5be4ac..d6afde4e849 100644 --- a/book/Makefile +++ b/book/Makefile @@ -14,7 +14,7 @@ BUILDDIR = _build prep: mkdir -p code/pulse - # cp -R $(FSTAR_HOME)/doc/book/code/* code + cp -R $(FSTAR_HOME)/doc/book/code/* code cp -R $(STEEL_HOME)/share/steel/examples/pulse/by-example/* code/pulse diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 65f00b288e3..d5b83066df6 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -82,4 +82,4 @@ dependent types, refinement types, etc. will be of use again. pulse_user_defined_predicates pulse_conditionals pulse_loops - + pulse_arrays diff --git a/book/pulse/pulse_arrays.rst b/book/pulse/pulse_arrays.rst new file mode 100644 index 00000000000..e91c06c8f26 --- /dev/null +++ b/book/pulse/pulse_arrays.rst @@ -0,0 +1,192 @@ +.. _Pulse_Arrays: + +Mutable Arrays +=============== + +In this chapter, we will learn about mutable arrays in Pulse. An array +is a contiguous collection of values of same type. Similar to ``ref``, +arrays in Pulse can be allocated in the stack frame of the current function +or in the heap---while the stack allocated arrays are reclaimed automatically +(e.g., when the function returns), heap allocated arrays are explicitly managed +by the program. + +Pulse provides two array types: ``Pulse.Lib.Array.array t`` as the basic array type +and ``Pulse.Lib.Vec.vec t`` for heap allocated arrays. To provide code reuse, functions +that may operate over both stack and heap allocated arrays can be written using +``Pulse.Lib.Array.array t``---the ``Pulse.Lib.Vec`` library provides back-and-forth coercions +between ``vec t`` and ``array t``. + +``array t`` +^^^^^^^^^^^^ + +We illustrate the basics of ``array t`` with the help of the following example +that reads an array: + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: ```pulse //readi$ + :end-before: ``` + +The library provides a points-to predicate ``pts_to arr s`` with the interpretation that +in the current memory, the contents of ``arr`` are same as the (functional) sequence ``s`` +(``s:FStar.Seq.seq t``). Arrays can be read and written-to using indexes of type ``FStar.SizeT.t``, +a model of C ``size_t`` in F*, provided that the index is within the array bounds. + +Since the representation sequence is specificational, the ``s`` argument to ``read`` +is erased. Further, the refinement ``SZ.v i < Seq.length s`` enforces that the index +is in bounds. The function returns the ``i``-th element of the array, the +asserted by the postcondition vprop ``pure (x == Seq.index s (SZ.v i))``. The body +of the function uses the array read operator ``arr.(i)``. + +As another example, let's write to the ``i``-th element of an array: + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: ```pulse //writei$ + :end-before: ``` + +The function uses the array write operator ``arr(i) <- x`` and the postcondition +asserts that in the state when the function returns, the contents of the array +are same as the sequence ``s`` updated at the index ``i``. + +The ``pts_to`` assertion supports fractional permissions, similar to the points-to +assertion for references. So far we have been writing ``pts_to arr s``, which is +a shorthand for ``pts_to #a arr #full_perm s``, i.e. exclusive read/write permission +on the array. While any permission suffices for reading, writing requires ``full_perm``. +For example, implementing ``write_i`` without ``full_perm`` will not work: + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: //writeipbegin$ + :end-before: //writeipend$ + +The library contains ``share`` and ``gather`` functions, similar to those for references, +to divide and combine permissions on arrays. + +We now look at a couple of examples that use arrays with conditionals, loops, existentials, +and invariants, tying up all the Pulse constructs we have seen so far. + +Compare +........ + +Let's implement a function that compares two arrays for equality: + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: //comparesigbegin$ + :end-before: //comparesigend$ + +The function takes two arrays ``a1`` and ``a2`` as input, and returns a boolean. +The postcondition ``pure (res <==> Seq.equal 's1 's2)`` +specifies that the boolean is true if and only if the sequence representations of the +two arrays are equal. Since the function only reads the arrays, it is parametric in the +permissions ``p1`` and ``p2`` on the two arrays. Note that the type parameter ``t`` has +type :ref:`eqtype`, requiring that values of type ``t`` support +decidable equality. + +One way to implement ``compare`` is to use a ``while`` loop, reading the two arrays +using a mutable counter and checking that the corresponding elements are equal. + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: //compareimplbegin$ + :end-before: //compareimplend$ + +The loop invariant states that (a) the arrays are pointwise equal up to the current value +of the counter, and (b) the boolean ``b`` is true if and only if the current value +of the counter is less than the length of the arrays and the arrays are equal at that index. +While (a) helps proving the final postcondition of ``compare``, (b) is required to maintain the +invariant after the counter is incremented in the loop body. + +Copy +..... + +As our next example, let's implement a ``copy`` function that copies the contents +of the array ``a2`` to ``a1``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: //copy$ + :end-before: ``` + +The loop invariant existentially abstracts over the contents of ``a1``, and maintains +that upto the current loop counter, the contents of the two arrays are equal. Rest of +the code is straightforward, the loop conditional checks that the loop counter is less +than the array lengths and the loop body copies one element at a time. + +The reader will notice that the postcondition of ``copy`` is a little convoluted. +A better signature would be the following, where we directly state that the +contents of ``a1`` are same as ``'s2``: + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: //copy2sigbegin$ + :end-before: //copy2sigend$ + +We can implement this signature, but it requires one step of rewriting at the end +after the ``while`` loop to get the postcondition in this exact shape: + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: //copy2rewriting$ + :end-before: //copy2rewritingend$ + +A ``rewrite`` explicits rewrites a context vprop; Pulse checker checks that the +rewriting is sound. We will learn about ``rewrite`` in later chapters. + + +Stack allocated arrays +^^^^^^^^^^^^^^^^^^^^^^^ + +Stack arrays can be allocated using the expression ``[| v; n |]``. It allocates +an array of size ``n``, with all the array elements initialized to ``v``. +It provides the postcondition that the newly create array points to a length ``n`` +sequence of ``v``. The following example allocates two arrays on the stack and +compares them using the ``compare`` function above. + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: ```pulse //compare_stack_arrays$ + :end-before: ``` + +As with the stack references, stack arrays don't need to be deallocated or +dropped, they are reclaimed automatically when the function returns. As a result, +returning them from the function is not allowed: + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: //ret_stack_array$ + :end-before: //ret_stack_array_end$ + +Heap allocated arrays +^^^^^^^^^^^^^^^^^^^^^^ + +Pulse library ``Pulse.Lib.Vec`` provides the heap allocated array type ``vec t``. +Similar to ``array``, ``vec`` is accompanied with a ``pts_to`` assertion with support +for fractional permissions, ``share`` and ``gather`` for dividing and combining permissions, +and read and write functions. However, unlike ``array``, the ``Vec`` library provides allocation +and free functions. + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: //heaparray$ + :end-before: //heaparrayend$ + +As with the heap references, heap allocated arrays can be coerced to ``array`` using the coercion +``vec_to_array``. To use the coercion, it is often required to convert ``Vec.pts_to`` to ``Array.pts_to`` +back-and-forth; the library provides ``to_array_pts_to`` and ``to_vec_pts_to`` lemmas for this purpose. + +The following example illustrates the pattern. It copies the contents of a stack array into a heap array, +using the ``copy2`` function we wrote above. + +.. literalinclude:: ../code/pulse/PulseTutorial.Array.fst + :language: pulse + :start-after: ```pulse //copyuse$ + :end-before: ``` + +Note how the assertion for ``v`` transforms from ``V.pts_to`` to ``pts_to`` (the points-to assertion +for arrays) and back. It means that array algorithms and routines can be implemented with the +``array t`` type, and then can be reused for both stack- and heap-allocated arrays. + +Finally, though the name ``vec a`` evokes the Rust ``std::Vec`` library, we don't yet support automatic +resizing. From b4ef084d88a3ec3e2b73d0b42ce60171eda50a78 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 5 Jan 2024 10:19:06 -0800 Subject: [PATCH 18/65] some light edits in array chapter --- book/part3/part3_interfaces.rst | 2 + book/pulse/pulse.rst | 4 +- book/pulse/pulse_arrays.rst | 86 ++++++++++++-------- book/pulse/pulse_loops.rst | 27 +++--- book/pulse/pulse_user_defined_predicates.rst | 2 + 5 files changed, 73 insertions(+), 48 deletions(-) diff --git a/book/part3/part3_interfaces.rst b/book/part3/part3_interfaces.rst index ac3e59b46a1..0f2eeee29de 100644 --- a/book/part3/part3_interfaces.rst +++ b/book/part3/part3_interfaces.rst @@ -24,6 +24,8 @@ To see how interfaces work, we'll look at the design of the **bounded integer** modules ``FStar.UInt32``, ``FStar.UInt64``, and the like, building our own simplified versions by way of illustration. +.. _Machine_integers: + Bounded Integers ^^^^^^^^^^^^^^^^ diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index d5b83066df6..b6f49601abb 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -64,8 +64,8 @@ dependent types, refinement types, etc. will be of use again. Why is it called Pulse? Because it's based on a logic called Steel, and one of the authors and his daughter are big fans of a classic reggae band called `Steel Pulse `_. We - wanted a name that was softer than Steel, more human, and, well, a - bit playful. So, Pulse! + wanted a name that was softer than Steel, and, well, a bit + playful. So, Pulse! diff --git a/book/pulse/pulse_arrays.rst b/book/pulse/pulse_arrays.rst index e91c06c8f26..96456e8fc74 100644 --- a/book/pulse/pulse_arrays.rst +++ b/book/pulse/pulse_arrays.rst @@ -4,11 +4,11 @@ Mutable Arrays =============== In this chapter, we will learn about mutable arrays in Pulse. An array -is a contiguous collection of values of same type. Similar to ``ref``, +is a contiguous collection of values of the same type. Similar to ``ref``, arrays in Pulse can be allocated in the stack frame of the current function or in the heap---while the stack allocated arrays are reclaimed automatically (e.g., when the function returns), heap allocated arrays are explicitly managed -by the program. +by the programmer. Pulse provides two array types: ``Pulse.Lib.Array.array t`` as the basic array type and ``Pulse.Lib.Vec.vec t`` for heap allocated arrays. To provide code reuse, functions @@ -27,16 +27,24 @@ that reads an array: :start-after: ```pulse //readi$ :end-before: ``` -The library provides a points-to predicate ``pts_to arr s`` with the interpretation that -in the current memory, the contents of ``arr`` are same as the (functional) sequence ``s`` -(``s:FStar.Seq.seq t``). Arrays can be read and written-to using indexes of type ``FStar.SizeT.t``, -a model of C ``size_t`` in F*, provided that the index is within the array bounds. - -Since the representation sequence is specificational, the ``s`` argument to ``read`` -is erased. Further, the refinement ``SZ.v i < Seq.length s`` enforces that the index -is in bounds. The function returns the ``i``-th element of the array, the -asserted by the postcondition vprop ``pure (x == Seq.index s (SZ.v i))``. The body -of the function uses the array read operator ``arr.(i)``. +The library provides a points-to predicate ``pts_to arr #p s`` with +the interpretation that in the current memory, the contents of ``arr`` +are same as the (functional) sequence ``s:FStar.Seq.seq t``. Like the +``pts_to`` predicate on reference, it is also indexed by an implicit +fractional permission ``p``, which distinguished shared, read-only +access from exclusive read/write access. + +In the arguments of ``read_i``, the argument ```s`` is erased, since +it is for specification only. + +Arrays can be read and written-to using indexes of type +``FStar.SizeT.t``, a model of C ``size_t`` [#]_ in F*, provided that +the index is within the array bounds---the refinement ``SZ.v i < +Seq.length s`` enforces that the index is in bounds, where ``module SZ += FStar.SizeT``. The function returns the ``i``-th element of the +array, the asserted by the postcondition vprop ``pure (x == Seq.index +s (SZ.v i))``. The body of the function uses the array read operator +``arr.(i)``. As another example, let's write to the ``i``-th element of an array: @@ -49,23 +57,29 @@ The function uses the array write operator ``arr(i) <- x`` and the postcondition asserts that in the state when the function returns, the contents of the array are same as the sequence ``s`` updated at the index ``i``. -The ``pts_to`` assertion supports fractional permissions, similar to the points-to -assertion for references. So far we have been writing ``pts_to arr s``, which is -a shorthand for ``pts_to #a arr #full_perm s``, i.e. exclusive read/write permission -on the array. While any permission suffices for reading, writing requires ``full_perm``. -For example, implementing ``write_i`` without ``full_perm`` will not work: +While any permission suffices for reading, writing requires +``full_perm``. For example, implementing ``write_i`` without +``full_perm`` is rejected, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst :language: pulse :start-after: //writeipbegin$ :end-before: //writeipend$ -The library contains ``share`` and ``gather`` functions, similar to those for references, -to divide and combine permissions on arrays. +The library contains ``share`` and ``gather`` functions, similar to +those for references, to divide and combine permissions on arrays. -We now look at a couple of examples that use arrays with conditionals, loops, existentials, -and invariants, tying up all the Pulse constructs we have seen so far. +We now look at a couple of examples that use arrays with conditionals, +loops, existentials, and invariants, using many of the Pulse +constructs we have seen so far. +.. [#] ``size_t`` in C is an unsigned integer type that is at least + ``16`` bits wide. The upper bound of ``size_t`` is platform + dependent. ``FStar.SizeT.size_t`` models this type and is + extracted to the primitive ``size_t`` type in C, similar to the + other :ref:`bounded integer types ` discussed + previously. + Compare ........ @@ -131,18 +145,20 @@ after the ``while`` loop to get the postcondition in this exact shape: :start-after: //copy2rewriting$ :end-before: //copy2rewritingend$ -A ``rewrite`` explicits rewrites a context vprop; Pulse checker checks that the -rewriting is sound. We will learn about ``rewrite`` in later chapters. +We could also rewrite the predicates explicitly, as we saw in a +:ref:`previous chapter `. Stack allocated arrays ^^^^^^^^^^^^^^^^^^^^^^^ -Stack arrays can be allocated using the expression ``[| v; n |]``. It allocates -an array of size ``n``, with all the array elements initialized to ``v``. -It provides the postcondition that the newly create array points to a length ``n`` -sequence of ``v``. The following example allocates two arrays on the stack and -compares them using the ``compare`` function above. +Stack arrays can be allocated using the expression ``[| v; n |]``. It +allocates an array of size ``n``, with all the array elements +initialized to ``v``. The size ``n`` must be compile-time constant. +It provides the postcondition that the newly create array points to a +length ``n`` sequence of ``v``. The following example allocates two +arrays on the stack and compares them using the ``compare`` function +above. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst :language: pulse @@ -161,11 +177,15 @@ returning them from the function is not allowed: Heap allocated arrays ^^^^^^^^^^^^^^^^^^^^^^ -Pulse library ``Pulse.Lib.Vec`` provides the heap allocated array type ``vec t``. -Similar to ``array``, ``vec`` is accompanied with a ``pts_to`` assertion with support -for fractional permissions, ``share`` and ``gather`` for dividing and combining permissions, -and read and write functions. However, unlike ``array``, the ``Vec`` library provides allocation -and free functions. +The library ``Pulse.Lib.Vec`` provides the type ``vec t``, for +heap-allocated arrays: ``vec`` is to ``array`` as ``box`` is to +``ref``. + +Similar to ``array``, ``vec`` is accompanied with a ``pts_to`` +assertion with support for fractional permissions, ``share`` and +``gather`` for dividing and combining permissions, and read and write +functions. However, unlike ``array``, the ``Vec`` library provides +allocation and free functions. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst :language: pulse diff --git a/book/pulse/pulse_loops.rst b/book/pulse/pulse_loops.rst index 108722546c3..71402486a40 100755 --- a/book/pulse/pulse_loops.rst +++ b/book/pulse/pulse_loops.rst @@ -37,17 +37,6 @@ Where - the postcondition of the entire loop is ``invariant false``. -Countdown -......... - -Here's our first Pulse program with a loop: ``count_down`` repeatedly -decrements a reference until it reaches ``0``. - -.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst - :language: pulse - :start-after: ```pulse //count_down$ - :end-before: ``` - One way to understand the invariant is that it describes program assertions at three different program points. @@ -59,10 +48,22 @@ assertions at three different program points. * when ``b`` is undetermined, the invariant describes the property of the program state just before the guard is (re)executed, i.e., - at the entry to the loop and at end of loop body. + at the entry to the loop and at the end of loop body. Coming up with an invariant to describe a loop often requires some -careful thinking. This one is relatively easy, hopefully intuitive. +careful thinking. We'll see many examples in the remaining chapters, +starting with some simple loops here. + +Countdown +......... + +Here's our first Pulse program with a loop: ``count_down`` repeatedly +decrements a reference until it reaches ``0``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: pulse + :start-after: ```pulse //count_down$ + :end-before: ``` While loops in Pulse are perhaps a bit more general than in other languages. The ``guard`` is an arbitrary Pulse program, not just a diff --git a/book/pulse/pulse_user_defined_predicates.rst b/book/pulse/pulse_user_defined_predicates.rst index 6fa1cfd99f4..f18cbd8a219 100644 --- a/book/pulse/pulse_user_defined_predicates.rst +++ b/book/pulse/pulse_user_defined_predicates.rst @@ -127,6 +127,8 @@ This allows type inference to work better, as shown below. :start-after: ```pulse //move_alt$ :end-before: ``` +.. _Pulse_rewriting: + Rewriting ......... From 35cfbc545e9db3f59444c1c6156e43e3c4480973 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 5 Jan 2024 16:12:00 -0800 Subject: [PATCH 19/65] recursion, plus some tweaks --- book/part4/part4_ghost.rst | 2 + book/pulse/pulse.rst | 1 + book/pulse/pulse_conditionals.rst | 1 + book/pulse/pulse_loops.rst | 83 +++++++++++++++----- book/pulse/pulse_user_defined_predicates.rst | 4 +- 5 files changed, 72 insertions(+), 19 deletions(-) diff --git a/book/part4/part4_ghost.rst b/book/part4/part4_ghost.rst index 4174c8d5ff2..0cd5c61e340 100644 --- a/book/part4/part4_ghost.rst +++ b/book/part4/part4_ghost.rst @@ -360,6 +360,8 @@ A few comments on these examples: type ``erased nat`` when the type ``nat`` was expected. +.. _Ghost_in_total_contexts: + Using Ghost Computations in Total Contexts ------------------------------------------ diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index b6f49601abb..9a96d6adb18 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -83,3 +83,4 @@ dependent types, refinement types, etc. will be of use again. pulse_conditionals pulse_loops pulse_arrays + pulse_ghost diff --git a/book/pulse/pulse_conditionals.rst b/book/pulse/pulse_conditionals.rst index d314f531af5..51ec19cb5c1 100644 --- a/book/pulse/pulse_conditionals.rst +++ b/book/pulse/pulse_conditionals.rst @@ -181,6 +181,7 @@ condition hypothesis for the preceding branches not taken. We plan to enhance the Pulse checker to also provide these negated path conditions. +.. _Pulse_nullable_ref_helpers: Helpers +++++++ diff --git a/book/pulse/pulse_loops.rst b/book/pulse/pulse_loops.rst index 71402486a40..f7fb73b053f 100755 --- a/book/pulse/pulse_loops.rst +++ b/book/pulse/pulse_loops.rst @@ -1,8 +1,17 @@ -Loops -###### +.. _Pulse_Loops: + +Loops & Recursion +################# In this chapter, we'll see how various looping constructs work in -Pulse, starting with ``while``. +Pulse, starting with ``while`` and also recursive functions. + +By default, Pulse's logic is designed for partial correctness. This +means that programs are allowed to loop forever. When we say that +program returns ``v:t`` satisfying a postcondition ``p``, this should +be understood to mean that the program could loop forever, but if it +does return, it is guaranteed to return a ``v:t`` where the state +satisfies ``p v``. While loops: General form @@ -55,7 +64,7 @@ careful thinking. We'll see many examples in the remaining chapters, starting with some simple loops here. Countdown -......... ++++++++++ Here's our first Pulse program with a loop: ``count_down`` repeatedly decrements a reference until it reaches ``0``. @@ -79,16 +88,9 @@ variable. Partial correctness -................... - -By default, Pulse's logic is designed for partial correctness. This -means that programs are allowed to loop forever. When we say that -program returns ``v:t`` satisfying a postcondition ``p``, this should -be understood to mean that the program could loop forever, but if it -does return, it is guaranteed to return a ``v:t`` where the state -satisfies ``p v``. ++++++++++++++++++++ -This partial correctness interpretation means that the following +The partial correctness interpretation means that the following infinitely looping variant of our program is also accepted: .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst @@ -106,7 +108,7 @@ general purpose sequential programs (i.e., no concurrency) that is also terminating. Multiply by repeated addition -............................. ++++++++++++++++++++++++++++++ Our next program with a loop multiplies two natural numbers ``x, y`` by repeatedly adding ``y`` to an accumulator ``x`` times. This @@ -135,7 +137,7 @@ A few noteworthy points: loop continues so long as the counter is strictly less than ``x``. Summing the first ``N`` numbers -............................... ++++++++++++++++++++++++++++++++ This next example shows a Pulse program that sums the first ``n`` natural numbers. It illustrates how Pulse programs can developed along @@ -173,7 +175,52 @@ but with a couple of differences: the proof is concluded. The program is a bit artificial, but hopefully it illustrates how -Pulse programs can be shown to first F* specificational program, and -then to rely on mathematical reasoning on those specificational -programs to conclude properties about the Pulse program itself. +Pulse programs can be shown to first refine a pure F* function, and +then to rely on mathematical reasoning on those pure functions to +conclude properties about the Pulse program itself. +Recursion +......... + +Pulse also supports general recursion, i.e., recursive functions that +may not terminate. Here is a simple example---we'll see more examples +later. + +Let's start with a standard F* (doubly) recursive definition that +computes the nth fibonacci number. + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: fstar + :start-after: //fib$ + :end-before: //fib$ + +One can also implement it in Pulse, as ``fib_rec`` while using an +out-parameter to hold that values of the last two fibonacci numbers in +the sequence. + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: pulse + :start-after: //fib_rec$ + :end-before: ``` + +Some points to note here: + + * Recursive definitions in Pulse are introduced with ``fn rec``. + + * So that we can easily memoize the last two values of ``fib``, we + expect the argument ``n`` to be a positive number, rather than + also allowing ``0``. + + * A quirk shown in the comments: We need an additional type + annotation to properly type ``(1, 1)`` as a pair of nats. + +Of course, one can also define fiboncacci iteratively, with a while +loop, as shown below. + +.. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst + :language: pulse + :start-after: //fib_loop$ + :end-before: ``` + + + diff --git a/book/pulse/pulse_user_defined_predicates.rst b/book/pulse/pulse_user_defined_predicates.rst index f18cbd8a219..8c42cd77781 100644 --- a/book/pulse/pulse_user_defined_predicates.rst +++ b/book/pulse/pulse_user_defined_predicates.rst @@ -6,7 +6,9 @@ User-defined Predicates In addition to the vprop predicates and connectives that the Pulse libraries provide, users very commonly define their own ``vprops``. We show a few simple examples here---subsequent examples will make heavy -use of user-defined predicates. +use of user-defined predicates. For example, see this section for +:ref:`recursively defined predicates `. + Fold and Unfold with Diagonal Pairs From 713a679eac7a96b453b5ebf9405aad3298cb5e5c Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Mon, 8 Jan 2024 17:17:12 -0800 Subject: [PATCH 20/65] higher order functions and some linked list, starting on @==> --- book/.gitignore | 1 + book/pulse/pulse.rst | 2 + book/pulse/pulse_ghost.rst | 361 ++++++++++++++++++++ book/pulse/pulse_higher_order.rst | 132 +++++++ book/pulse/pulse_implication_and_forall.rst | 172 ++++++++++ 5 files changed, 668 insertions(+) create mode 100755 book/pulse/pulse_ghost.rst create mode 100755 book/pulse/pulse_higher_order.rst create mode 100755 book/pulse/pulse_implication_and_forall.rst diff --git a/book/.gitignore b/book/.gitignore index 45d59899c3f..b567567d98d 100644 --- a/book/.gitignore +++ b/book/.gitignore @@ -1,3 +1,4 @@ __pycache__ code _build +*~ \ No newline at end of file diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 9a96d6adb18..64a1b612bab 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -84,3 +84,5 @@ dependent types, refinement types, etc. will be of use again. pulse_loops pulse_arrays pulse_ghost + pulse_higher_order + pulse_implication_and_forall diff --git a/book/pulse/pulse_ghost.rst b/book/pulse/pulse_ghost.rst new file mode 100755 index 00000000000..e6c7bc7f992 --- /dev/null +++ b/book/pulse/pulse_ghost.rst @@ -0,0 +1,361 @@ +.. _Pulse_Ghost: + +Ghost Computations +================== + +Throughout the chapters on pure F*, we made routine use of the +``Lemmas`` and ghost functions to prove properties of our +programs. Lemmas, you will recall, are pure, total functions that +always return ``unit``, i.e., they have no computational significance +and are erased by the F* compiler. F* :ref:`ghost functions +` are also pure, total functions, except that they are +allowed to inspect erased values in a controlled way---they too are +erased by the F* compiler. + +As we've seen already, F* lemmas and ghost functions can be directly +used in Pulse code. But, these are only useful for describing +properties about the pure values in scope. Often, in Pulse, one needs +to write lemmas that speak about the state, manipulate ``vprops``, +etc. For this purpose, Pulse provides its own notion of *ghost +computations* (think of these as the analog of F* lemmas and ghost +functions, except they are specified using ``vprops``); and *ghost +state* (think of these as the analog of F* erased types, except ghost +state is mutable, though still computationally irrelevant). Ghost +computations are used everywhere in Pulse---we've already seen a few +example. Ghost state is especially useful in proofs of concurrent +programs. + + +Ghost Functions +............... + +Here's a Pulse function that fails to check, with the error message +below. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //incr_erased_non_ghost$ + :end-before: //incr_erased_non_ghost$ + +.. code-block:: + + Cannot bind ghost expression reveal x with ST computation + +We should expect this to fail, since the program claims to be able to +compute an integer ``y`` by incrementing an erased integer ``x``---the +``x:erased int`` doesn't exist at runtime, so this program cannot be +compiled. + +But, if we tag the function with the ``ghost`` qualifier, then this +works: + + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //incr_erased$ + :end-before: ``` + +The ``ghost`` qualifier indicates the the Pulse checker that the +function is to be erased at runtime, so ``ghost`` functions are +allowed to make use of F* functions with ``GTot`` effect, like +``FStar.Ghost.reveal``. + +However, for this to be sound, no compilable code is allowed to depend +on the return value of a ``ghost`` function. So, the following code +fails with the error below: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //try_use_incr_erased$ + :end-before: //try_use_incr_erased$ + +.. code-block:: + + Expected a term with a non-informative (e.g., erased) type; got int + +That is, when calling a ``ghost`` function from a non-ghost context, +the return type of the ghost function must be non-informative, e.g, +``erased``, or ``unit`` etc. The class of non-informative types and +the rules for allowing F* :ref:`ghost computations to be used in total +contexts is described here `, and the same +rules apply in Pulse. + +To use of ``incr_erased`` in non-ghost contexts, we have to erased its +result. There are a few ways of doing this. + +Here's a verbose but explicit way, where we define a nested ghost +function to wrap the call to ``incr_erased``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //use_incr_erased$ + :end-before: //use_incr_erased$ + +The library also contains ``Pulse.Lib.Pervasives.call_ghost`` that is +a higher-order combinator to erased the result of a ghost call. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //use_incr_erased_alt$ + :end-before: ``` + +The ``call_ghost`` combinator can be used with ghost functions of +different arities, though it requires the applications to be curried +in the following way. + +Suppose we have a binary ghost function, like ``add_erased``: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //add_erased$ + :end-before: ``` + +To call it in a non-ghost context, one can do the following: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //use_add_erased$ + :end-before: ``` + +That said, since ``ghost`` functions must have non-informative return +types to be usable in non-ghost contexts, it's usually best to define +them that way to start with, rather than having to wrap them at each +call site, as shown below: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //add_erased_erased$ + :end-before: ``` + + +Some Primitive Ghost Functions +.............................. + +Pulse ghost functions with ``emp`` or ``pure _`` pre and +postconditions are not that interesting---such functions can usually +be written with regular F* ghost functions. + +Ghost functions are aften used as proof steps to prove equivalences +among ``vprops``. We saw a few :ref:`examples of ghost functions +before `---they are ghost since their +implementations are compositions of ``ghost`` functions from the Pulse +library. + +The ``rewrite`` primitive that we saw :ref:`previously +` is in fact a defined function in the Pulse +library. Its signature looks like this: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //__rewrite_sig$ + :end-before: //__rewrite_sig$ + +Many of the other primitives like ``fold``, ``unfold``, etc. are +defined in terms of ``rewrite`` and are ``ghost`` computations. + +Other primitives like ``introduce exists*`` are also implemented in +terms of library ``ghost`` functions, with signatures like the one +below: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //intro_exists_sig$ + :end-before: //intro_exists_sig$ + + +.. _Pulse_recursive_predicates: + +Recursive Predicates and Ghost Lemmas +..................................... + +We previously saw how to :ref:`define custom predicates +`, e.g., for representation predicates on data +structures. Since a ``vprop`` is just a regular type, one can also +define ``vprops`` by recursion in F*. Working with these recursive +predicates in Pulse usually involves writing recursive ghost functions +as lemmas. We'll look at a simple example of this here and revisit in +subsequent chapters as look at programming unbounded structures, like +linked lists. + +Say you have a list of references and want to describe that they all +contain integers whose value is at most ``n``. The recursive predicate +``all_at_most l n`` does just that: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: fstar + :start-after: //all_at_most$ + :end-before: //all_at_most$ + +As we did when working with :ref:`nullable references +`, it's useful to define a few helper +ghost functions to introduce and eliminate this predicate, for each of +its cases. + +The elimination and introduction functions for the ``[]`` case are +very similar to what had previously with nullable references: + + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //intro_all_at_most_nil$ + :end-before: ``` + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //elim_all_at_most_nil$ + :end-before: ``` + +For the ``hd::tl`` case, we have to be a bit more careful in how we do +the rewriting: using a regular ``rewrite`` fails, since it involves +proving an equality between two higher-order terms (the body of +``exists*`` is represented in F* as a lambda term). Pulse also +provides a ``rewrite_by`` function in the library that allows +rewriting terms using a user-provided tactic. In this case, the tactic +``Pulse.Lib.Pervasives.vprop_equiv_norm`` equates ``vprops`` by +proving them definitionally equal by normalization. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //intro_all_at_most_cons$ + :end-before: ``` + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //elim_all_at_most_cons$ + :end-before: ``` + +Recursive Ghost Lemmas +++++++++++++++++++++++ + +Pulse allows writing recursive ghost functions as lemmas for use in +Pulse code. Like F* lemmas, recursive ghost functions must be proven +to terminate on all inputs---otherwise, they would not be sound. + +To see this in action, let's write a ghost function to prove that +``all_at_most l n`` can be weakened to ``all_at_most l m`` when ``n <= +m``. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //weaken_at_most$ + :end-before: ``` + +A few points to note: + + * Recursive functions in Pulse are defined using ``fn rec``. + + * Ghost recursive functions must also have a ``decreases`` + annotation---unlike in F*, Pulse does not yet attempt to infer a + default decreases annotation. In this case, we are recursing on + the list ``l``. + + * List patterns in Pulse do not (yet) have the same syntactic sugar + as in F*, i.e., you cannot write ``[]`` and ``hd::tl`` as + patterns. + + * The proof itself is fairly straightforward: + + - In the ``Nil`` case, we eliminate the ``all_at_most`` predicate + at ``n`` and introduce it at ``m``. + + - In the ``Cons`` case, we eliminate ``all_at_most l n``,` use the + induction hypothesis to weaken the ``all_at_most`` predicate on + the ``tl``; and then introduce it again, packaging it with + assumption on ``hd``. + +Mutable Ghost References +........................ + +The underlying logic that Pulse is based on actually supports a very +general form of ghost state based on partial commutative monoids +(PCMs). Users can define their own ghost state abstractions in F* +using PCMs and use these in Pulse programs. The library +``Pulse.Lib.GhostReference`` provides the simplest and most common +form of ghost state: references to erased values with a +fractional-permission-based ownership discipline. + +We'll use the module abbreviation ``module GR = +Pulse.Lib.GhostReference`` in what follows. The library is very +similar to ``Pulse.Lib.Reference``, in that it provides: + + * ``GR.ref a``: The main type of ghost references. ``GR.ref`` is an + erasable type and is hence considered non-informative. + + * ``GR.pts_to (#a:Type0) (r:GR.ref a) (#p:perm) (v:a) : vprop`` is + the main predicate provided by the library. Similar to the regular + ``pts_to``, the permission index defaults to ``full_perm``. + + * Unlike ``ref a`` (and more like ``box a``), ghost references + ``GR.ref a`` are not lexically scoped: they are allocated using + ``GR.alloc`` and freed using ``GR.free``. Of course, neither + allocation nor free'ing has any runtime cost---these are just + ghost operations. + + * Reading a ghost reference using ``!r`` returns an ``erased a``, + when ``r:GR.ref a``. Likewise, to update ``r``, it is enough to + provide a new value ``v:erased a``. + + * Operations to ``share`` and ``gather`` ghost references work just + as with ``ref``. + +A somewhat contrived example +++++++++++++++++++++++++++++ + +Must examples that require ghost state usually involve stating +interesting invariants between multiple threads, or sometimes in a +sequential setting to correlate knowledge among different +components. We'll see examples of that in a later chapter. For now, +here's a small example that gives a flavor of how ghost state can be +used. + +Suppose we want to give a function read/write access to a reference, +but want to ensure that before it returns, it resets the value of the +reference to its original value. The simplest way to do that would be +to give the function the following signature: + +.. code-block:: pulse + + fn uses_but_resets #a (x:ref a) + requires pts_to x 'v + ensures pts_to x 'v + +Here's another way to do it, this time with ghost references. + +First, we define a predicate ``correlated`` that holds full permission +to a reference and half permission to a ghost reference, forcing them +to hold the same value. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //correlated$ + :end-before: //correlated$ + +Now, here's the signature of a function ``use_temp``: at first glance, +from its signature alone, one might think that the witness ``v0`` +bound in the precondition is unrelated to the ``v1`` bound in the +postcondition. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //use_temp_sig$ + :end-before: //use_temp_sig$ + +But, ``use_temp`` only has half-permission to the ghost reference and +cannot mutate it. So, although it can mutate the reference itself, in +order to return its postcondition, it must reset the reference to its +initial value. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //use_temp_body$ + :end-before: //use_temp_body$ + +This property can be exploited by a caller to pass a reference to +``use_temp`` and be assured that the value is unchanged when it +returns. + +.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst + :language: pulse + :start-after: //use_correlated$ + :end-before: ``` + diff --git a/book/pulse/pulse_higher_order.rst b/book/pulse/pulse_higher_order.rst new file mode 100755 index 00000000000..52feab5e759 --- /dev/null +++ b/book/pulse/pulse_higher_order.rst @@ -0,0 +1,132 @@ +.. _Pulse_higher_order: + +Higher Order Functions +====================== + +Like F*, Pulse is higher order. That is, Pulse functions are first +class and can be passed to other functions, returned as results, and +some functions can even be stored in the heap. + +Pulse Computation Types +....................... + +Here's perhaps the simplest higher-order function: ``apply`` abstracts +function application. + +.. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst + :language: pulse + :start-after: //apply$ + :end-before: ``` + +This function is polymorphic in the argument, result type, pre, and +post-condition of a function ``f``, which it applies to an argument +``x``. This is the first time we have written the type of a Pulse +function as an F* type. So far, we have been writing *signatures* of +Pulse functions, using the ``fn/requires/ensures`` notation, but here +we see that the type of Pulse function is of the form: + +.. code-block:: pulse + + x:a -> stt b pre (fun y -> post) + +where, + + * like any F* function type, Pulse functions are dependent and the + right hand side of the arrow can mention ``x`` + + * immediately to the right of the arrow is a Pulse computation type + tag, similar to F*'s ``Tot``, or ``GTot``, etc. + + * The tag ``stt`` is the most permissive of Pulse computation type + tags, allowing the function's body to read and write the state, + run forever etc., but with pre-condition ``pre``, return type + ``b``, and post-condition ``fun y -> post``. + +Pulse provides several other kinds of computation types. For now, the +most important is the constructor for ghost computations. We show +below ``apply_ghost``, the analog of ``apply`` but for ``ghost`` +functions. + +.. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst + :language: pulse + :start-after: //apply_ghost$ + :end-before: ``` + +The type of ``f`` is similar to what we had before, but this time we +have: + + * compuation type tag ``stt_ghost``, indication that this function + reads or writes ghost state only, and always terminates. + + * the return type is ``b x`` + + * the next argument is ``emp_inames``, describes the set of + invariants that a computation may open, where ``emp_inames`` means + that this computation opens no invariants. For now, let's ignore + this. + + * the precondition is ``pre x`` and the postcondition is ``fun y -> + post x y``. + +Counters +........ + +For a slightly more interesting use of higher order programming, let's +look at how to program a mutable counter. We'll start by defining the +type ``ctr`` of a counter. + +.. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst + :language: fstar + :start-after: //ctr$ + :end-before: //ctr$ + +A counter packages the following: + + * A predicate ``inv`` on the state, where ``inv i`` states that the + current value of the counter is ``i``, without describing exactly + how the counter's state is implemented. + + * A stateful function ``next`` that expects the ``inv i``, returns + the current value ``i`` of the counter, and provides ``inv (i + + 1)``. + + * A stateful function ``destroy`` to deallocate the counter. + +One way to implement a ``ctr`` is to represent the state with a +heap-allocated reference. This is what ``new_counter`` does below: + +.. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst + :language: pulse + :start-after: //new_counter$ + :end-before: ``` + +Here's how it works. + +First, we allocate a new heap reference ``x`` initialized to ``0``. + +Pulse allows us to define functions within any scope. So, we define +two local functions ``next`` and ``destroy``, whose implementations +and specifications are straightforward. The important bit is that they +capture the reference ``x:box int`` in their closure. + +Finally, we package ``next`` and ``destroy`` into a ``c:ctr``, +instanting ``inv`` to ``Box.pts_to x``, rewrite the context assertion +to ``c.inv 0``, and return ``c``. + +In a caller's context, such as ``test_counter`` below, the fact that +the counter is implemented using a single mutable heap reference is +completely hidden. + +.. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst + :language: pulse + :start-after: //test_counter$ + :end-before: ``` + + + + + + + + + diff --git a/book/pulse/pulse_implication_and_forall.rst b/book/pulse/pulse_implication_and_forall.rst new file mode 100755 index 00000000000..2b5d067e3a7 --- /dev/null +++ b/book/pulse/pulse_implication_and_forall.rst @@ -0,0 +1,172 @@ +.. _Pulse_implication_and_forall: + +Implication and Universal Quantification +======================================== + +In this chapter, we'll learn about two more separation logic +connectives, ``@==>`` and ``forall*``. We'll explain how they work in +the context of a linked list library. + +Representing a Linked List +.......................... + +Let's start by defining the type of a singly linked list: + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: fstar + :start-after: //llist$ + :end-before: //llist$ + +A ``node t`` contains a ``head:t`` and a ``tail:llist t``, a nullable +reference pointing to the rest of the list. Nullable references are +represented by an option, as :ref:`we saw before +`. + +Next, we need a predicate to relate a linked list to a logical +representation of the list, for use in specifications. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: fstar + :start-after: //is_list$ + :end-before: //is_list$ + +The predicate ``is_list x l`` is a recursive predicate: + + * When ``l == []``, the reference ``x`` must be null. + + * Otherwise, ``l == head :: tl``, ``x`` must contains a valid + reference ``p``, where ``p`` points to ``{ head; tail }`` and, + recursively , we have ``is_list tail tl``. + + +Boilerplate: Introducing and Eliminating ``is_list`` +++++++++++++++++++++++++++++++++++++++++++++++++++++ + +We've seen :ref:`recursive predicates in a previous chapter +`, and just as we did there, we need some +helper ghost functions to work with ``is_list``. We expect the Pulse +checker will automate these boilerplate ghost lemmas in the future, +but, for now, we are forced to write them by hand. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //boilerplate$ + :end-before: //boilerplate$ + + + +Case analyzing a nullable pointer ++++++++++++++++++++++++++++++++++ + +When working with a linked list, the first thing we'll do, typically, +is to check whether a given ``x:llist t`` is null or not. However, the +``is_list x l`` predicate is defined by case analysing ``l:list t`` +rather than ``x:llist t``, since that is makes it possible to write +the predicate by recursing on the tail of ``l``. So, below, we have a +predicate ``is_list_cases x l`` that inverts ``is_list x l`` predicate +based on whether or not ``x`` is null. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: fstar + :start-after: //is_list_cases$ + :end-before: //is_list_cases$ + +Next, we define a ghost function to invert ``is_list`` into ``is_list_cases``. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //cases_of_is_list$ + :end-before: ``` + +We also define two more ghost functions that package up the call to +``cases_of_is_list``. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //is_list_case_none$ + :end-before: ``` + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //is_list_case_some$ + :end-before: ``` + +Length, Recursively ++++++++++++++++++++ + +With our helper functions in hand, let's get to writing some real +code, starting with a function to compute the length of an ``llist``. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //length$ + :end-before: ``` + +The ``None`` case is simple. + +Some points to note in the ``Some`` case: + + * We use ``with _node _tl. _`` to "get our hands on" the + existentially bound witnesses. + + * After reading ``let node = !vl``, we need ``is_list node.tail + _tl`` to make the recursive call. But the context contains + ``is_list _node.tail _tl`` and ``node == _node``. So, we need a + rewrite. + + * We re-introduce the ``is_list`` predicate, and return ``1 + + n``. While the ``intro_is_list_cons x vl`` is a ghost step and + will be erased before execution, the addition is not---so, this + function is not tail recursive. + +Exercise 1 +++++++++++ + +Write a tail-recursive version of ``length``. + +Exercise 2 +++++++++++ + +Index the ``is_list`` predicate with a fractional permission. Write +ghost functions to share and gather fractional ``is_list`` predicates. + +Length, Iteratively, with ``@==>`` +++++++++++++++++++++++++++++++++++ + +What if we wanted to implement ``length`` using a while loop, as is +more idiomatic in a language like C. It will take us a few steps to +get there, and we'll use the ``@==>`` operator to structure our proof. + +The library ``module I = Pulse.Lib.Stick.Util`` defines the operator +``(@==>)`` and utilities for using it. Here's an informal description +of what it means: + +``p @==> q`` says that if you have ``p`` then you can *trade* it for +``q``. In other words, from ``p ** (p @==> q)``, you can derive +``q``. This step of reasoning is performed using a ghost function +``I.elim`` with the signature below: + +.. code-block:: pulse + + ghost + fn I.elim (p q:vprop) + requires p ** (p @==> q) + ensures q + + +For now, think of ``p @==> q`` as a kind of implication in separation +logic and ``I.elim`` as implication elimination. Importantly, if you +think of ``p`` as describing permission on a resource, the ``I.elim`` +makes you *give up* the permission ``p`` and get ``q`` as a +result. Note, during this step, you also lose permission on the +implication, i.e., ``p @==> q`` lets you trade ``p`` for ``q`` just +once. + +But, how to you create a ``p @==> q`` in the first place? That's its +introducion form, shown below: + +.. code-block:: pulse + + ghost + fn I.intro (p q r:vprop) (elim: () + From febf3c6208f5bf3d7575b93365f0aee546f53b5c Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Tue, 9 Jan 2024 15:32:46 -0800 Subject: [PATCH 21/65] higher order, implication and forall --- book/pulse/pulse.rst | 1 + book/pulse/pulse_higher_order.rst | 2 +- book/pulse/pulse_implication_and_forall.rst | 317 +++++++++++++------- 3 files changed, 203 insertions(+), 117 deletions(-) diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 64a1b612bab..20d31d61aa7 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -86,3 +86,4 @@ dependent types, refinement types, etc. will be of use again. pulse_ghost pulse_higher_order pulse_implication_and_forall + pulse_linked_list diff --git a/book/pulse/pulse_higher_order.rst b/book/pulse/pulse_higher_order.rst index 52feab5e759..3b690590ab3 100755 --- a/book/pulse/pulse_higher_order.rst +++ b/book/pulse/pulse_higher_order.rst @@ -110,7 +110,7 @@ and specifications are straightforward. The important bit is that they capture the reference ``x:box int`` in their closure. Finally, we package ``next`` and ``destroy`` into a ``c:ctr``, -instanting ``inv`` to ``Box.pts_to x``, rewrite the context assertion +instantiating ``inv`` to ``Box.pts_to x``, rewrite the context assertion to ``c.inv 0``, and return ``c``. In a caller's context, such as ``test_counter`` below, the fact that diff --git a/book/pulse/pulse_implication_and_forall.rst b/book/pulse/pulse_implication_and_forall.rst index 2b5d067e3a7..7602658328c 100755 --- a/book/pulse/pulse_implication_and_forall.rst +++ b/book/pulse/pulse_implication_and_forall.rst @@ -4,169 +4,254 @@ Implication and Universal Quantification ======================================== In this chapter, we'll learn about two more separation logic -connectives, ``@==>`` and ``forall*``. We'll explain how they work in -the context of a linked list library. +connectives, ``@==>`` and ``forall*``. We show a few very simple +examples using them, though these will be almost trivial. In the next +chapter, on linked lists, we'll see more significant uses of these +connectives. -Representing a Linked List -.......................... +Separating Ghost Implication +............................ -Let's start by defining the type of a singly linked list: +The library ``module I = Pulse.Lib.Stick.Util`` defines the operator +``(@==>)`` and utilities for using it. In the literature, the operator +``p --* q`` is pronounces "p magic-wand q"; ``p @==> q`` is similar, +though there are some important technical differences, as we'll +see. We'll just pronounce it ``p implies q``, knowing that in this +context, we are referring to a separation logic implication rather +than F*'s usual ``p ==> q``. -.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst - :language: fstar - :start-after: //llist$ - :end-before: //llist$ +Here's an informal description of what ``p @==> q`` means: + + ``p @==> q`` says that if you have ``p`` then you can *trade* it for + ``q``. In other words, from ``p ** (p @==> q)``, you can derive + ``q``. This step of reasoning is performed using a ghost function + ``I.elim`` with the signature below: + + .. code-block:: pulse + + ghost + fn I.elim (p q:vprop) + requires p ** (p @==> q) + ensures q + -A ``node t`` contains a ``head:t`` and a ``tail:llist t``, a nullable -reference pointing to the rest of the list. Nullable references are -represented by an option, as :ref:`we saw before -`. +Importantly, if you think of ``p`` as describing permission on a +resource, the ``I.elim`` makes you *give up* the permission ``p`` and +get ``q`` as a result. Note, during this step, you also lose +permission on the implication, i.e., ``p @==> q`` lets you trade ``p`` +for ``q`` just once. -Next, we need a predicate to relate a linked list to a logical -representation of the list, for use in specifications. +But, how do you create a ``p @==> q`` in the first place? That's its +introduction form, shown below: + + .. code-block:: pulse + + ghost + fn I.intro (p q r:vprop) + (elim: unit -> stt_ghost unit emp_inames (r ** p) (fun _ -> q)) + requires r + ensures p @==> q + +That is, to introduce ``p @==> q``, one has to show hold permission +``r``, such that a ghost function can transform ``r ** p`` into +``q``. + +Share and Gather +++++++++++++++++ + +Here's a small example to see ``p @==> q`` at work. .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: fstar - :start-after: //is_list$ - :end-before: //is_list$ + :start-after: //regain_half$ + :end-before: //regain_half$ -The predicate ``is_list x l`` is a recursive predicate: +The predicate ``regain_half`` says that you can trade a +half-permission ``pts_to x #one_half v`` for a full permission +``pts_to x v``. At first, this may seem counterintuitive: how can you +gain a full permission from half-permission. The thing to remember is +that ``p @==> q`` itself holds permissions internally. In particular, +``regain_half x v`` holds ``exists* u. pts_to x #one_half u`` +internally, such that if the context presents the other half, the +eliminator can combine the two to return the full permission. - * When ``l == []``, the reference ``x`` must be null. +Let's look at how to introduce ``regain_half``: - * Otherwise, ``l == head :: tl``, ``x`` must contains a valid - reference ``p``, where ``p`` points to ``{ head; tail }`` and, - recursively , we have ``is_list tail tl``. +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //intro_regain_half$ + :end-before: ``` +The specification says that if we start out with ``pts_to x 'v`` then +we can split it into ``pts_to x #one_half v`` and a ``regain_half x +'v``. The normal way of splitting a permission a reference would split +it into two halves---here, we just package the second half in a ghost +function that allows us to gather the permission back when we need it. -Boilerplate: Introducing and Eliminating ``is_list`` -++++++++++++++++++++++++++++++++++++++++++++++++++++ +In the implementation, we define an auxiliary ghost function that +corresponds to the eliminator fo ``pts_to x #one_half 'v @==> pts_to x +'v``---it's just a ``gather``. Then, we split ``pts_to x 'v`` into +halves, call ``I.intro`` passing the eliminator, and the fold it into +a ``regain_half``. All ``regain_half`` has done is to package the +ghost function ``aux``, together the half permission on ``x``, and put +it into a ``vprop``. -We've seen :ref:`recursive predicates in a previous chapter -`, and just as we did there, we need some -helper ghost functions to work with ``is_list``. We expect the Pulse -checker will automate these boilerplate ghost lemmas in the future, -but, for now, we are forced to write them by hand. +Later on, if want to use ``regain_half``, we can call its +eliminator---which, effectively, calls ``aux`` with the needed +permission, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: pulse - :start-after: //boilerplate$ - :end-before: //boilerplate$ - + :start-after: //use_regain_half$ + :end-before: ``` +At this point, you may be wondering why we bother to use a +``regain_half x 'v`` in the first place, since one might as well have +just used ``pts_to x #one_half 'v`` and ``gather``, and you'd be right +to wonder that! In this simple usage, the ``(@==>)`` implication +hasn't bought us much. -Case analyzing a nullable pointer -+++++++++++++++++++++++++++++++++ +Universal Quantification +........................ -When working with a linked list, the first thing we'll do, typically, -is to check whether a given ``x:llist t`` is null or not. However, the -``is_list x l`` predicate is defined by case analysing ``l:list t`` -rather than ``x:llist t``, since that is makes it possible to write -the predicate by recursing on the tail of ``l``. So, below, we have a -predicate ``is_list_cases x l`` that inverts ``is_list x l`` predicate -based on whether or not ``x`` is null. +Let's look at our ``regain_half`` predicate again: .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: fstar - :start-after: //is_list_cases$ - :end-before: //is_list_cases$ + :start-after: //regain_half$ + :end-before: //regain_half$ -Next, we define a ghost function to invert ``is_list`` into ``is_list_cases``. +This predicate is not as general as it could be: to eliminate it, it +requires the caller to prove that they holds ``pts_to x #one_half v``, +for the same ``v`` as was used when the implication was introduced. -.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst - :language: pulse - :start-after: //cases_of_is_list$ - :end-before: ``` +One could try to generalize ``regain_half`` a bit by changing it to: -We also define two more ghost functions that package up the call to -``cases_of_is_list``. +.. code-block:: fstar -.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst - :language: pulse - :start-after: //is_list_case_none$ - :end-before: ``` + let regain_half #a (x:GR.ref a) (v:a) = + (exists* u. pts_to x #one_half u) @==> pts_to x v + +This is an improvement, but it still is not general enough, since it +does not relate ``v`` to the existentially bound ``u``. What we really +need is a universal quantifier. + +Here's the right version of ``regain_half``: .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst - :language: pulse - :start-after: //is_list_case_some$ - :end-before: ``` + :language: fstar + :start-after: //regain_half_q$ + :end-before: //regain_half_q$ -Length, Recursively -+++++++++++++++++++ +This says that no matter what ``pts_to x #one_half u`` the context +has, they can recover full permission to it, *with the same witness* +``u``. -With our helper functions in hand, let's get to writing some real -code, starting with a function to compute the length of an ``llist``. +The ``forall*`` quantifier and utilities to manipulate it are defined +in ``Pulse.Lib.Forall.Util``. The introduction and elimination forms +have a similar shape to what we saw earlier for ``@==>``: -.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst - :language: pulse - :start-after: //length$ - :end-before: ``` -The ``None`` case is simple. + .. code-block:: pulse -Some points to note in the ``Some`` case: + ghost + fn FA.elim (#a:Type) (#p:a->vprop) (v:a) + requires (forall* x. p x) + ensures p v - * We use ``with _node _tl. _`` to "get our hands on" the - existentially bound witnesses. +The eliminator allows a *single* instantiation of the universally +bound ``x`` to ``v``. - * After reading ``let node = !vl``, we need ``is_list node.tail - _tl`` to make the recursive call. But the context contains - ``is_list _node.tail _tl`` and ``node == _node``. So, we need a - rewrite. + .. code-block:: pulse - * We re-introduce the ``is_list`` predicate, and return ``1 + - n``. While the ``intro_is_list_cons x vl`` is a ghost step and - will be erased before execution, the addition is not---so, this - function is not tail recursive. + ghost + fn FA.intro (#a:Type) (#p:a->vprop) + (v:vprop) + (f_elim : (x:a -> stt_ghost unit emp_inames v (fun _ -> p x))) + requires v + ensures (forall* x. p x) -Exercise 1 -++++++++++ +The introduction form requires proving that one holds ``v``, and that +with ``v`` a ghost function can produce ``p x``, for any ``x``. -Write a tail-recursive version of ``length``. - -Exercise 2 -++++++++++ +Note, it's very common to have universal quantifiers and implications +together, so the library also provides the following combined forms: -Index the ``is_list`` predicate with a fractional permission. Write -ghost functions to share and gather fractional ``is_list`` predicates. + .. code-block:: pulse -Length, Iteratively, with ``@==>`` -++++++++++++++++++++++++++++++++++ + ghost + fn elim_forall_imp (#a:Type0) (p q: a -> vprop) (x:a) + requires (forall* x. p x @==> q x) ** p x + ensures q x + +and -What if we wanted to implement ``length`` using a while loop, as is -more idiomatic in a language like C. It will take us a few steps to -get there, and we'll use the ``@==>`` operator to structure our proof. + .. code-block:: pulse + + ghost + fn intro_forall_imp (#a:Type0) (p q: a -> vprop) (r:vprop) + (elim: (u:a -> stt_ghost unit emp_inames + (r ** p u) + (fun _ -> q u))) + requires r + ensures forall* x. p x @==> q x -The library ``module I = Pulse.Lib.Stick.Util`` defines the operator -``(@==>)`` and utilities for using it. Here's an informal description -of what it means: + +Share and Gather, Again ++++++++++++++++++++++++ -``p @==> q`` says that if you have ``p`` then you can *trade* it for -``q``. In other words, from ``p ** (p @==> q)``, you can derive -``q``. This step of reasoning is performed using a ghost function -``I.elim`` with the signature below: +Here's how one introduces ``regain_half_q``: -.. code-block:: pulse - - ghost - fn I.elim (p q:vprop) - requires p ** (p @==> q) - ensures q - +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //intro_regain_half_q$ + :end-before: ``` + +Now, when we want to use it, we can trade in any half-permission on +``pts_to x #one_half u``, for a full permission with the same ``u``. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //use_regain_half_q$ + :end-before: ``` -For now, think of ``p @==> q`` as a kind of implication in separation -logic and ``I.elim`` as implication elimination. Importantly, if you -think of ``p`` as describing permission on a resource, the ``I.elim`` -makes you *give up* the permission ``p`` and get ``q`` as a -result. Note, during this step, you also lose permission on the -implication, i.e., ``p @==> q`` lets you trade ``p`` for ``q`` just -once. +Note using the eliminator for ``FA.elim`` is quite verbose: we need to +specify the quantifier term again. The way Pulse uses F*'s unifier +currently does not allow it to properly find solutions to some +higher-order unification problems. We expect to fix this soon. -But, how to you create a ``p @==> q`` in the first place? That's its -introducion form, shown below: -.. code-block:: pulse +More than implications: Ghost steps +................................... - ghost - fn I.intro (p q r:vprop) (elim: () +As a final example in this section, we show that one can use package +any ghost computation into a ``@==>``, including steps that may modify +the ghost state. In full generality, this makes ``@==>`` behave more +like a view shift (in Iris terminology) than a wand. +Here's a predicate ``can_update`` which says that one can trade a half +permission to ``pts_to x #one_half u`` for a full permission to a +*different value* ``pts_to x v``. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: fstar + :start-after: //can_update$ + :end-before: //can_update$ + +In ``make_can_update``, we package a ghost-state update function into +a binary quantifier ``forall* u v. ...``. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //make_can_update$ + :end-before: ``` + +And in ``update``, below, we instantiate it to update the reference +``x`` from ``'u`` to ``k``, and also return back a ``can_update`` +predicate to the caller, for further use. + +.. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst + :language: pulse + :start-after: //update$ + :end-before: ``` + From 11cc829b5317c25716f1b427a4fe3805e09412ed Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Tue, 9 Jan 2024 18:41:52 -0800 Subject: [PATCH 22/65] trades, linked list --- book/pulse/pulse_implication_and_forall.rst | 29 +-- book/pulse/pulse_linked_list.rst | 249 ++++++++++++++++++++ 2 files changed, 262 insertions(+), 16 deletions(-) create mode 100755 book/pulse/pulse_linked_list.rst diff --git a/book/pulse/pulse_implication_and_forall.rst b/book/pulse/pulse_implication_and_forall.rst index 7602658328c..4724544836c 100755 --- a/book/pulse/pulse_implication_and_forall.rst +++ b/book/pulse/pulse_implication_and_forall.rst @@ -9,17 +9,14 @@ examples using them, though these will be almost trivial. In the next chapter, on linked lists, we'll see more significant uses of these connectives. -Separating Ghost Implication -............................ +Trades, or Separating Ghost Implication +........................................ The library ``module I = Pulse.Lib.Stick.Util`` defines the operator -``(@==>)`` and utilities for using it. In the literature, the operator -``p --* q`` is pronounces "p magic-wand q"; ``p @==> q`` is similar, -though there are some important technical differences, as we'll -see. We'll just pronounce it ``p implies q``, knowing that in this -context, we are referring to a separation logic implication rather -than F*'s usual ``p ==> q``. - +*trade* ``(@==>)`` and utilities for using it. In the literature, the +operator ``p --* q`` is pronounced "p magic-wand q"; ``p @==> q`` is +similar, though there are some important technical differences, as +we'll see. We'll just pronounce it ``p for q``, or ``p trades for q``. Here's an informal description of what ``p @==> q`` means: ``p @==> q`` says that if you have ``p`` then you can *trade* it for @@ -108,8 +105,8 @@ permission, as shown below. At this point, you may be wondering why we bother to use a ``regain_half x 'v`` in the first place, since one might as well have just used ``pts_to x #one_half 'v`` and ``gather``, and you'd be right -to wonder that! In this simple usage, the ``(@==>)`` implication -hasn't bought us much. +to wonder that! In this simple usage, the ``(@==>)`` hasn't bought us +much. Universal Quantification ........................ @@ -123,7 +120,7 @@ Let's look at our ``regain_half`` predicate again: This predicate is not as general as it could be: to eliminate it, it requires the caller to prove that they holds ``pts_to x #one_half v``, -for the same ``v`` as was used when the implication was introduced. +for the same ``v`` as was used when the trade was introduced. One could try to generalize ``regain_half`` a bit by changing it to: @@ -174,7 +171,7 @@ bound ``x`` to ``v``. The introduction form requires proving that one holds ``v``, and that with ``v`` a ghost function can produce ``p x``, for any ``x``. -Note, it's very common to have universal quantifiers and implications +Note, it's very common to have universal quantifiers and trades together, so the library also provides the following combined forms: .. code-block:: pulse @@ -221,11 +218,11 @@ currently does not allow it to properly find solutions to some higher-order unification problems. We expect to fix this soon. -More than implications: Ghost steps -................................... +Trades and Ghost Steps +...................... As a final example in this section, we show that one can use package -any ghost computation into a ``@==>``, including steps that may modify +any ghost computation into a trade, including steps that may modify the ghost state. In full generality, this makes ``@==>`` behave more like a view shift (in Iris terminology) than a wand. diff --git a/book/pulse/pulse_linked_list.rst b/book/pulse/pulse_linked_list.rst new file mode 100755 index 00000000000..f1ea189e3af --- /dev/null +++ b/book/pulse/pulse_linked_list.rst @@ -0,0 +1,249 @@ +.. _Pulse_linked_list: + +Linked Lists +============ + +In this chapter, we develop a linked list library. Along the way, +we'll see uses of recursive predicates, trades, and universal +quantification. + +Representing a Linked List +.......................... + +Let's start by defining the type of a singly linked list: + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: fstar + :start-after: //llist$ + :end-before: //llist$ + +A ``node t`` contains a ``head:t`` and a ``tail:llist t``, a nullable +reference pointing to the rest of the list. Nullable references are +represented by an option, as :ref:`we saw before +`. + +Next, we need a predicate to relate a linked list to a logical +representation of the list, for use in specifications. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: fstar + :start-after: //is_list$ + :end-before: //is_list$ + +The predicate ``is_list x l`` is a recursive predicate: + + * When ``l == []``, the reference ``x`` must be null. + + * Otherwise, ``l == head :: tl``, ``x`` must contains a valid + reference ``p``, where ``p`` points to ``{ head; tail }`` and, + recursively , we have ``is_list tail tl``. + + +Boilerplate: Introducing and Eliminating ``is_list`` +.................................................... + +We've seen :ref:`recursive predicates in a previous chapter +`, and just as we did there, we need some +helper ghost functions to work with ``is_list``. We expect the Pulse +checker will automate these boilerplate ghost lemmas in the future, +but, for now, we are forced to write them by hand. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //boilerplate$ + :end-before: //boilerplate$ + + + +Case analyzing a nullable pointer +................................. + +When working with a linked list, the first thing we'll do, typically, +is to check whether a given ``x:llist t`` is null or not. However, the +``is_list x l`` predicate is defined by case analysing ``l:list t`` +rather than ``x:llist t``, since that is makes it possible to write +the predicate by recursing on the tail of ``l``. So, below, we have a +predicate ``is_list_cases x l`` that inverts ``is_list x l`` predicate +based on whether or not ``x`` is null. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: fstar + :start-after: //is_list_cases$ + :end-before: //is_list_cases$ + +Next, we define a ghost function to invert ``is_list`` into ``is_list_cases``. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //cases_of_is_list$ + :end-before: ``` + +We also define two more ghost functions that package up the call to +``cases_of_is_list``. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //is_list_case_none$ + :end-before: ``` + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //is_list_case_some$ + :end-before: ``` + +Length, Recursively +................... + +With our helper functions in hand, let's get to writing some real +code, starting with a function to compute the length of an ``llist``. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //length$ + :end-before: ``` + +The ``None`` case is simple. + +Some points to note in the ``Some`` case: + + * We use ``with _node _tl. _`` to "get our hands on" the + existentially bound witnesses. + + * After reading ``let node = !vl``, we need ``is_list node.tail + _tl`` to make the recursive call. But the context contains + ``is_list _node.tail _tl`` and ``node == _node``. So, we need a + rewrite. + + * We re-introduce the ``is_list`` predicate, and return ``1 + + n``. While the ``intro_is_list_cons x vl`` is a ghost step and + will be erased before execution, the addition is not---so, this + function is not tail recursive. + +Exercise 1 +.......... + +Write a tail-recursive version of ``length``. + +Exercise 2 +.......... + +Index the ``is_list`` predicate with a fractional permission. Write +ghost functions to share and gather fractional ``is_list`` predicates. + +Length, Iteratively, with Trades +................................ + +What if we wanted to implement ``length`` using a while loop, as is +more idiomatic in a language like C. It will take us a few steps to +get there, and we'll use the trade operator (``@==>``) to structure +our proof. + +Trade Tails ++++++++++++ + +Our first step is to define ``tail_for_cons``, a lemma that with +permission on a node pointer (``pts_to v n``), we can build a trade +transforming a permission on the tail into a permission for a cons +cell starting at the given node. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //tail_for_cons$ + :end-before: ``` + + +Tail of a list +++++++++++++++ + +Next, here's a basic operation on a linked list: given a pointer to a +cons cell, return a pointer to its tail. Here's a small diagram: + + +.. code-block:: + + x tl + | | + v v + .---.---. .---.---. + | | --|---> | | --|--> ... + .---.---. .---.---. + +We're given a pointer ``x`` to the cons cell at the head of a list, +and we want to return ``tl``, the pointer to the next cell (or +``None``, of x this is the end of the list). But, if we want to return +a pointer to ``tl``, we a permission accounting problem: + + * We cannot return permission to ``x`` to the caller, since then we + would have two *aliases* pointing to the next cell in the list: + the returned ``tl`` and ``x -> next``. + + * But, we cannot consume the permission to ``x`` either, since we + would like to return permission to ``x`` once the return ``tl`` + goes out of scope. + +The solution here is to use a trade. The type of ``tail`` below says +that if ``x`` is a non-null pointer satisfying ``is_list x 'l``, then +``tail`` returns a pointer ``y`` such that ``is_list y tl`` (where +``tl`` is the tail of ``'l``); and, one can trade ``is_list y tl`` to +recover permission to ``is_list x 'l``. The trade essentially says +that you cannot have permission to ``is_list x 'l`` and ``is_list y +tl`` at the same time, but once you give up permission on ``y``, you +can get back the original permission on ``x``. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //tail$ + :end-before: ``` + +``length_iter`` ++++++++++++++++ + +The code below shows our iterative implementation of ``length``. The +basic idea is simple , thorugh the proof takes a bit of doing. We +initialize a current pointer ``cur`` to the head of the list; and +``ctr`` to ``0``. Then, while ``cur`` is not null, we move ``cur`` to +the tail and increment ``ctr``. Finally, we return the ``!ctr``. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //length_iter$ + :end-before: ``` + +Now, for the proof. The main part is the loop invariant, which says: + + * the current value of the counter is ``n``; + + * ``cur`` holds a list pointer, ``ll`` where ``ll`` contains the + list represented by ``suffix``; + + * ``n`` is the the length of the prefix of the list traversed so far; + + * the loop continues as long as ``b`` is true, i.e., the list + pointer ``l`` is not ``None``; + + * and, the key bit: you can trade ownership on ``ll`` back for + ownership on the original list ``x``. + +Some parts of this could be simplified, e.g., to avoid some of the +rewrites. + +One way to understand how trades have helped here is to compare +``length_iter`` to the recursive function ``length``. In ``length``, +after each recursive call returns, we called a ghost function to +repackage permission on the cons cell after taking out permission on +the tail. The recursive function call stack kept track of all these +pieces of ghost code that had to be executed. In the iterative +version, we use the trade to package up all the ghost functions that +need to be run, rather than using the call stack. When the loop +terminates, we use ``I.elim`` to run all that ghost code at once. + +Of course, the recursive ``length`` is much simpler in this case, but +this pattern of using trades to package up ghost functions is quite +broadly useful. + +Append, Recursively +................... + + +Append, Iteratively +................... From 8ac520a085436912a4a12f843a29b13d294bc1fb Mon Sep 17 00:00:00 2001 From: Aseem Rastogi Date: Wed, 10 Jan 2024 02:50:13 +0000 Subject: [PATCH 23/65] extraction --- book/pulse/pulse.rst | 1 + book/pulse/pulse_extraction.rst | 298 ++++++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 book/pulse/pulse_extraction.rst diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 9a96d6adb18..84cbda1a68e 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -84,3 +84,4 @@ dependent types, refinement types, etc. will be of use again. pulse_loops pulse_arrays pulse_ghost + pulse_extraction diff --git a/book/pulse/pulse_extraction.rst b/book/pulse/pulse_extraction.rst new file mode 100644 index 00000000000..098b4316295 --- /dev/null +++ b/book/pulse/pulse_extraction.rst @@ -0,0 +1,298 @@ +.. _Pulse_Extraction: + +Extraction +=========== + +Pulse programs can be extracted to OCaml, C, and Rust. + +Rust extraction +^^^^^^^^^^^^^^^^ + +.. note:: + The Rust extraction pipeline is under heavy development. + +We illustrate Rust extraction with the +`Boyer-Moore majority vote algorithm `_ implemented +in Pulse. The algorithm finds majority vote in an array of votes in linear time +(2n comparisons, where n is the length of the array) and constant extra memory. + +The algorithm consists of two phases. The first phase, called the pairing phase, +pairs off disagreeing votes (cancels them) until the remaining votes are all same. +The main idea of the algorithm is to do this pairing with n comparisons. After the +pairing phase, the remaining vote must be the majority, *if the majority exists*. +The second phase, called the counting phase, checks if the remaining vote is indeed +in majority with n more comparisons. + +We implement the algorithm in Pulse with the following specification: + +.. literalinclude:: ../code/pulse/PulseTutorial.Algorithms.fst + :language: pulse + :start-after: //majorityspec$ + :end-before: //majorityspecend$ + +The precondition ``SZ.fits (2 * SZ.v len)`` ensures safe arithmetic in the counting +phase of the algorithm. The implementation of the function contains two while loops +for the two phases. + +For the first phase, the algorithm maintains three auxiliary variables, ``i`` for the +loop counter, ``cand`` the current majority candidate, and a count ``k``. For the ``i``-th +element of the array, if ``k = 0``, the algorithm assigns the ``i``-th vote as the new +majority candidate and assigns ``k = ``. Otherwise, if the ``i``-th vote is same as +``cand``, it increments ``k`` by one, otherwise it decrements ``k`` by one. + + +.. literalinclude:: ../code/pulse/PulseTutorial.Algorithms.fst + :language: pulse + :start-after: //majorityphase1$ + :end-before: //majorityphase1end$ + +The loop invariant specifies majority constraints *within* the prefix of the array +that the loop has visited so far. The second phase after this is a simple counting loop. +We refer the reader to the corresponding Pulse file for more details. + +To extract ``majority`` to Rust, we first invoke F* extraction pipeline with option +``--codegen Extension``. This emits a ``.ast`` file containing an internal AST +representation of the file. Pulse framework is accompanied with a rust extration tool +that takes as input the ``.ast`` files and outputs the extracted Rust code (by-default +the output is written to ``stdout``, if an ``-o `` option is provided to the tool, +the output is written to ``file``). The output of the tool on this example is as shown +below: + +.. literalinclude:: ../code/pulse/voting.rs + :language: pulse + :start-after: //majorityrust$ + :end-before: //majorityrustend$ + +We can output this code in a file, and then test it as follows: + +.. literalinclude:: ../code/pulse/voting.rs + :language: pulse + :start-after: //majorityrusttest$ + :end-before: //majorityrusttestend$ + +A few notes about the extracted Rust code: + +- The Pulse function and the Rust function are generic in the type of the votes. In Rust, + the extracted code required the type argument to implement the ``Clone``, ``Copy``, and + ``PartialEq`` traits. Currently we hardcode these traits. We plan to specify these traits + in Pulse through attribute mechanism + +- The ghost arguments ``p`` and ``s`` appear in the Rust code as ``unit`` arguments, we plan + to make it so that these arguments are completely erased. + +- Whereas ``majority`` needs only read permission for the ``votes`` array in the Pulse + signature, the extracted Rust code specifies the argument as ``&mut``. The Rust extraction + pipeline currently passes all the references as ``mut``, we plan to make + it more precise by taking into account the permissions from the Pulse signature. + + +.. Mutable Arrays +.. =============== + +.. In this chapter, we will learn about mutable arrays in Pulse. An array +.. is a contiguous collection of values of the same type. Similar to ``ref``, +.. arrays in Pulse can be allocated in the stack frame of the current function +.. or in the heap---while the stack allocated arrays are reclaimed automatically +.. (e.g., when the function returns), heap allocated arrays are explicitly managed +.. by the programmer. + +.. Pulse provides two array types: ``Pulse.Lib.Array.array t`` as the basic array type +.. and ``Pulse.Lib.Vec.vec t`` for heap allocated arrays. To provide code reuse, functions +.. that may operate over both stack and heap allocated arrays can be written using +.. ``Pulse.Lib.Array.array t``---the ``Pulse.Lib.Vec`` library provides back-and-forth coercions +.. between ``vec t`` and ``array t``. + +.. ``array t`` +.. ^^^^^^^^^^^^ + +.. We illustrate the basics of ``array t`` with the help of the following example +.. that reads an array: + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: ```pulse //readi$ +.. :end-before: ``` + +.. The library provides a points-to predicate ``pts_to arr #p s`` with +.. the interpretation that in the current memory, the contents of ``arr`` +.. are same as the (functional) sequence ``s:FStar.Seq.seq t``. Like the +.. ``pts_to`` predicate on reference, it is also indexed by an implicit +.. fractional permission ``p``, which distinguished shared, read-only +.. access from exclusive read/write access. + +.. In the arguments of ``read_i``, the argument ```s`` is erased, since +.. it is for specification only. + +.. Arrays can be read and written-to using indexes of type +.. ``FStar.SizeT.t``, a model of C ``size_t`` [#]_ in F*, provided that +.. the index is within the array bounds---the refinement ``SZ.v i < +.. Seq.length s`` enforces that the index is in bounds, where ``module SZ +.. = FStar.SizeT``. The function returns the ``i``-th element of the +.. array, the asserted by the postcondition vprop ``pure (x == Seq.index +.. s (SZ.v i))``. The body of the function uses the array read operator +.. ``arr.(i)``. + +.. As another example, let's write to the ``i``-th element of an array: + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: ```pulse //writei$ +.. :end-before: ``` + +.. The function uses the array write operator ``arr(i) <- x`` and the postcondition +.. asserts that in the state when the function returns, the contents of the array +.. are same as the sequence ``s`` updated at the index ``i``. + +.. While any permission suffices for reading, writing requires +.. ``full_perm``. For example, implementing ``write_i`` without +.. ``full_perm`` is rejected, as shown below. + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: //writeipbegin$ +.. :end-before: //writeipend$ + +.. The library contains ``share`` and ``gather`` functions, similar to +.. those for references, to divide and combine permissions on arrays. + +.. We now look at a couple of examples that use arrays with conditionals, +.. loops, existentials, and invariants, using many of the Pulse +.. constructs we have seen so far. + +.. .. [#] ``size_t`` in C is an unsigned integer type that is at least +.. ``16`` bits wide. The upper bound of ``size_t`` is platform +.. dependent. ``FStar.SizeT.size_t`` models this type and is +.. extracted to the primitive ``size_t`` type in C, similar to the +.. other :ref:`bounded integer types ` discussed +.. previously. + +.. Compare +.. ........ + +.. Let's implement a function that compares two arrays for equality: + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: //comparesigbegin$ +.. :end-before: //comparesigend$ + +.. The function takes two arrays ``a1`` and ``a2`` as input, and returns a boolean. +.. The postcondition ``pure (res <==> Seq.equal 's1 's2)`` +.. specifies that the boolean is true if and only if the sequence representations of the +.. two arrays are equal. Since the function only reads the arrays, it is parametric in the +.. permissions ``p1`` and ``p2`` on the two arrays. Note that the type parameter ``t`` has +.. type :ref:`eqtype`, requiring that values of type ``t`` support +.. decidable equality. + +.. One way to implement ``compare`` is to use a ``while`` loop, reading the two arrays +.. using a mutable counter and checking that the corresponding elements are equal. + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: //compareimplbegin$ +.. :end-before: //compareimplend$ + +.. The loop invariant states that (a) the arrays are pointwise equal up to the current value +.. of the counter, and (b) the boolean ``b`` is true if and only if the current value +.. of the counter is less than the length of the arrays and the arrays are equal at that index. +.. While (a) helps proving the final postcondition of ``compare``, (b) is required to maintain the +.. invariant after the counter is incremented in the loop body. + +.. Copy +.. ..... + +.. As our next example, let's implement a ``copy`` function that copies the contents +.. of the array ``a2`` to ``a1``. + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: //copy$ +.. :end-before: ``` + +.. The loop invariant existentially abstracts over the contents of ``a1``, and maintains +.. that upto the current loop counter, the contents of the two arrays are equal. Rest of +.. the code is straightforward, the loop conditional checks that the loop counter is less +.. than the array lengths and the loop body copies one element at a time. + +.. The reader will notice that the postcondition of ``copy`` is a little convoluted. +.. A better signature would be the following, where we directly state that the +.. contents of ``a1`` are same as ``'s2``: + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: //copy2sigbegin$ +.. :end-before: //copy2sigend$ + +.. We can implement this signature, but it requires one step of rewriting at the end +.. after the ``while`` loop to get the postcondition in this exact shape: + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: //copy2rewriting$ +.. :end-before: //copy2rewritingend$ + +.. We could also rewrite the predicates explicitly, as we saw in a +.. :ref:`previous chapter `. + + +.. Stack allocated arrays +.. ^^^^^^^^^^^^^^^^^^^^^^^ + +.. Stack arrays can be allocated using the expression ``[| v; n |]``. It +.. allocates an array of size ``n``, with all the array elements +.. initialized to ``v``. The size ``n`` must be compile-time constant. +.. It provides the postcondition that the newly create array points to a +.. length ``n`` sequence of ``v``. The following example allocates two +.. arrays on the stack and compares them using the ``compare`` function +.. above. + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: ```pulse //compare_stack_arrays$ +.. :end-before: ``` + +.. As with the stack references, stack arrays don't need to be deallocated or +.. dropped, they are reclaimed automatically when the function returns. As a result, +.. returning them from the function is not allowed: + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: //ret_stack_array$ +.. :end-before: //ret_stack_array_end$ + +.. Heap allocated arrays +.. ^^^^^^^^^^^^^^^^^^^^^^ + +.. The library ``Pulse.Lib.Vec`` provides the type ``vec t``, for +.. heap-allocated arrays: ``vec`` is to ``array`` as ``box`` is to +.. ``ref``. + +.. Similar to ``array``, ``vec`` is accompanied with a ``pts_to`` +.. assertion with support for fractional permissions, ``share`` and +.. ``gather`` for dividing and combining permissions, and read and write +.. functions. However, unlike ``array``, the ``Vec`` library provides +.. allocation and free functions. + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: //heaparray$ +.. :end-before: //heaparrayend$ + +.. As with the heap references, heap allocated arrays can be coerced to ``array`` using the coercion +.. ``vec_to_array``. To use the coercion, it is often required to convert ``Vec.pts_to`` to ``Array.pts_to`` +.. back-and-forth; the library provides ``to_array_pts_to`` and ``to_vec_pts_to`` lemmas for this purpose. + +.. The following example illustrates the pattern. It copies the contents of a stack array into a heap array, +.. using the ``copy2`` function we wrote above. + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. :language: pulse +.. :start-after: ```pulse //copyuse$ +.. :end-before: ``` + +.. Note how the assertion for ``v`` transforms from ``V.pts_to`` to ``pts_to`` (the points-to assertion +.. for arrays) and back. It means that array algorithms and routines can be implemented with the +.. ``array t`` type, and then can be reused for both stack- and heap-allocated arrays. + +.. Finally, though the name ``vec a`` evokes the Rust ``std::Vec`` library, we don't yet support automatic +.. resizing. From 09e2af5b0ebbfdbb05124dec4fd536f52d4ddacd Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Tue, 9 Jan 2024 23:14:33 -0800 Subject: [PATCH 24/65] more linked list --- book/pulse/pulse_linked_list.rst | 96 ++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/book/pulse/pulse_linked_list.rst b/book/pulse/pulse_linked_list.rst index f1ea189e3af..c83e6dc2dc9 100755 --- a/book/pulse/pulse_linked_list.rst +++ b/book/pulse/pulse_linked_list.rst @@ -244,6 +244,102 @@ broadly useful. Append, Recursively ................... +Here's another recursive function on linked lists: ``append`` +concatenates ``y`` on to the end of ``x``. + +It's fairly straightforward: we recurse until we reach the last node +of ``x`` (i.e., the ``tail`` field is ``None``; and we set that field +to point to ``y``. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //append$ + :end-before: ``` + +The code is tail recursive in the ``Some _`` case, but notice that we +have a ghost function call *after* the recursive call. Like we did for +``length``, can we implement an iterative version of ``append``, +factoring this ghost code on the stack into a trade? Append, Iteratively ................... + +Let's start by defining a more general version of the ``tail`` +function from before. In comparison, the postcondition of ``tail_alt`` +uses a universal quantifier to say, rougly, that whatever list ``tl'`` +the returns ``y`` points to, it can be traded for a pointer to ``x`` +that cons's on to ``tl``. Our previous function ``tail`` can be easily +recovered by instantiating ``tl'`` to ``tl``. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //tail_alt$ + :end-before: ``` + +We'll use these quantified trades in our invariant of ``append_iter``, +shown below. The main idea of the implementation is to use a while +loop to traverse to the last element of the first list ``x``; and then +to set ``y`` as the ``next`` pointer of this last element. + +.. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst + :language: pulse + :start-after: //append_iter$ + :end-before: ``` + +There are few interesting points to note. + + * The main part is the quantified trade in the invariant, which, as + we traverse the list, encapsulates the ghost code that we need to + run at the end to restore permission to the initial list pointer + ``x``. + + * The library function, ``FA.trans_compose`` has the following + signature: + + .. code-block:: pulse + + ghost + fn trans_compose (#a #b #c:Type0) + (p: a -> vprop) + (q: b -> vprop) + (r: c -> vprop) + (f: a -> GTot b) + (g: b -> GTot c) + requires + (forall* x. p x @==> q (f x)) ** + (forall* x. q x @==> r (g x)) + ensures + forall* x. p x @==> r (g (f x)) + + + We use it in the key induction step as we move one step down the + list---similar to what we had in ``length_iter``, but this time + with a quantifier. + + * Illustrating again that Pulse is a superset of pure F*, we make + use of a :ref:`bit of F* sugar ` in the + ``introduce forall`` to prove a property needed for a Pulse + rewrite. + + * Finally, at the end of the loop, we use ``FA.elim_forall_imp`` to + restore permission on ``x``, now pointing to a concatenated list, + effectively running all the ghost code we accumulated as we + traversed the list. + +Perhaps the lesson from all this is that recursive programs are much +easier to write and prove correct that iterative ones? That's one +takeaway. But, hopefully, you've seen how trades and quantifiers work +and can be useful in some proofs, and, of course, we'll use them not +just for rewriting recursive as iterative code. + +Exercise 3 +++++++++++ + +Write a function to insert an element in a list and a specific +position. + + +Exercise 4 +++++++++++ + +Write a function to reverse a list. From 1b1952cb4bf6137a929cf73fab66f3e55edf3050 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Tue, 9 Jan 2024 23:19:40 -0800 Subject: [PATCH 25/65] tweaks --- book/pulse/pulse.rst | 4 ++-- book/pulse/pulse_linked_list.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index b8547bb36f5..5f59a43aaa2 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -16,8 +16,8 @@ features for building DSLs embedded in F* with customized syntax, type checker plugins, extraction support, etc., with *Pulse* as a showcase example of such a DSL. -Pulse is a new programming language embedded in F* and inheriting many -of its features (notably, it is higher order and has dependent types), +Pulse is a new programming language embedded in F*, inheriting many of +its features (notably, it is higher order and has dependent types), but with built-in support for programming with mutable state and concurrency, with specifications and proofs in `Concurrent Separation Logic `_. diff --git a/book/pulse/pulse_linked_list.rst b/book/pulse/pulse_linked_list.rst index c83e6dc2dc9..72bc8d6b3ba 100755 --- a/book/pulse/pulse_linked_list.rst +++ b/book/pulse/pulse_linked_list.rst @@ -329,8 +329,8 @@ There are few interesting points to note. Perhaps the lesson from all this is that recursive programs are much easier to write and prove correct that iterative ones? That's one takeaway. But, hopefully, you've seen how trades and quantifiers work -and can be useful in some proofs, and, of course, we'll use them not -just for rewriting recursive as iterative code. +and can be useful in some proofs---of course, we'll use them not just +for rewriting recursive as iterative code. Exercise 3 ++++++++++ From fac33bbf9617a74c48282ea8769096c4993b58db Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Wed, 10 Jan 2024 23:48:01 -0800 Subject: [PATCH 26/65] atomics & invariants --- book/fstar_pygments.py | 4 +- book/pulse/pulse.rst | 3 + book/pulse/pulse_atomics_and_invariants.rst | 315 ++++++++++++++++++++ book/pulse/pulse_higher_order.rst | 27 ++ book/pulse/pulse_parallel_increment.rst | 4 + book/pulse/pulse_spin_lock.rst | 4 + 6 files changed, 356 insertions(+), 1 deletion(-) create mode 100755 book/pulse/pulse_atomics_and_invariants.rst create mode 100755 book/pulse/pulse_parallel_increment.rst create mode 100755 book/pulse/pulse_spin_lock.rst diff --git a/book/fstar_pygments.py b/book/fstar_pygments.py index 236c1215ce6..87262fc06e8 100644 --- a/book/fstar_pygments.py +++ b/book/fstar_pygments.py @@ -104,7 +104,9 @@ class CustomLexer(RegexLexer): "atomic", "show_proof_state", "while", - "invariant" + "invariant", + "with_invariants", + "opens" ) class PulseLexer(RegexLexer): diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 5f59a43aaa2..cf155627b7b 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -87,4 +87,7 @@ dependent types, refinement types, etc. will be of use again. pulse_higher_order pulse_implication_and_forall pulse_linked_list + pulse_atomics_and_invariants + pulse_spin_lock + pulse_parallel_increment pulse_extraction diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst new file mode 100755 index 00000000000..ce12db5021d --- /dev/null +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -0,0 +1,315 @@ +.. _Pulse_atomics_and_invariants: + +Atomic Operations and Invariants +================================ + +In this section, we finally come to some concurrency related +constructs. + +Concurrency in Pulse is built around two concepts: + + * **Atomic operations**: operations that are guaranteed to be + executed in a single-step of computation without interruption by + other threads. + + * **Invariants**: named predicates that are enforced to be true at + all times. Atomic operations can make use of invariants, assuming + they are true in the current state, and enforced to be true again + once the atomic step concludes. + +Based on this, and in conjunction with all the other separation logic +constructs that we've learned about so far, notably the use of ghost +state, Pulse enables proofs of concurrent programs. + +Atomic Operations +................. + +We've learned so far about :ref:`two kinds of Pulse computations +`: + + * General purpose, partially correct computations, with the ``stt`` + computation type + + * Ghost computations, proven totally correct, and enforced to be + computatationally irrelevant with the ``stt_ghost`` computation + type. + +Pulse offers a third kind of computation, *atomic* computations, with +the ``stt_atomic`` computation type. Here is the signature of +``read_atomic`` and ``write_atomic`` from ``Pulse.Lib.Reference``: + +.. code-block:: pulse + + atomic + fn read_atomic (r:ref U32.t) (#n:erased U32.t) (#p:perm) + requires pts_to r #p n + returns x:U32.t + ensures pts_to r #p n ** pure (reveal n == x) + +.. code-block:: pulse + + atomic + fn write_atomic (r:ref U32.t) (x:U32.t) (#n:erased U32.t) + requires pts_to r n + ensures pts_to r x + +The ``atomic`` annotation on these functions claims that reading and +writing 32-bit integers can be done in a single atomic step of +computation. + +This is an assumption about the target architecture on which a Pulse +program is executed. It may be that on some machines, 32-bit values +cannot be read or written atomically. So, when using atomic +operations, you should be careful to check that its safe to assume +that these operations truly are atomic. + +Pulse also provides a way for you to declare that other operations are +atomic, e.g., maybe your machine supports 64-bit or 128-bit atomic +operations---you can program the semantics of these operations in F* +and add them to Pulse, marking them as atomic. + +Sometimes, particularly at higher order, you will see atomic +computations described by the computation type below: + +.. code-block:: fstar + + val stt_atomic (t:Type) (i:inames) (pre:vprop) (post:t -> vprop) + : Type u#2 + +Like ``stt_ghost``, atomic computations are total and live in universe +``u#2``. As such, you cannot store an atomic function in the state, +i.e., ``ref (unit -> stt_atomic t i p q)`` is not a well-formed type. + +Sometimes, we will also refer to the following computation type: + +.. code-block:: fstar + + val stt_unobservable (t:Type) (i:inames) (pre:vprop) (post:t -> vprop) + : Type u#2 + +Unobservable computations, or ``stt_unobservable``, are very closed +related to ghost computations, though are slightly different +technically---we'll learn more about these shortly. + +Atomic computations are also indexed by ``i:inames``, where ``inames`` +is a set of invariant names. We'll learn about these next. + +Invariants +.......... + +In ``Pulse.Lib.Core``, we have the following types: + +.. code-block:: fstar + + val inv (p:vprop) : Type u#0 + val iname : eqtype + val name_of_inv #p (i:inv p) : GTot iname + +The type ``inv p`` is the type of an *invariant*. Think of ``i:inv p`` +as a *token* which guarantees that ``p`` is true in the current state +and all future states of the program. Every invariant has a name, +``name_of_inv i``, though, the name is only relevant in +specifications, i.e., it is ghost. + +Creating an invariant ++++++++++++++++++++++ + +Let's start by looking at how to create an invariant. + +First, let's define a regular ``vprop``, ``owns x``, to mean that we +hold full-permission on ``x``. + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //owns$ + :end-before: ``` + +Now, if we can currently proves ``pts_to r x`` then we can turn it +into an invariant ``i:inv (owns r)``, as shown below. + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //create_invariant$ + :end-before: ``` + +Importantly, when we turn ``pts_to r x`` into ``inv (owns r)``, **we +lose** ownership of ``pts_to r x``. Remember, once we have ``inv (owns +r)``, Pulse's logic aims to that ``owns r`` remains true always. If we +were allowed to retain ``pts_to r x``, while also creating an ``inv +(owns r)``, we can clearly break the invariant, e.g., by freeing +``r``. + +.. note:: + + A tip: When using an ``inv p``, it's a good idea to make sure that + ``p`` is a user-defined predicate. For example, one might think to + just write ``inv (exists* v. pts_to x v)`` instead of defining an + auxiliary predicate for ``inv (owns r)``. However, the some of the + proof obligations produced by the Pulse checker are harder for the + SMT solver to prove if you don't use the auxiliary predicate and + you may start to see odd failures. This is something we're working + to improve. In the meantime, use an auxiliary predicate. + +``new_invariant`` is unobservable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The type of ``new_invariant`` is shown below: + +.. code-block:: fstar + + val new_invariant (p:vprop) + : stt_unobservable (inv p) emp_inames p (fun _ -> emp) + +The ``stt_unobservable`` says that ``new_invariant`` is an atomic step +of computation from Pulse's perspective, but it doesn't read or change +any observable state. In that regard, ``stt_unobservable`` is a lot +like ``stt_ghost``; however, while ``stt_ghost`` computations are +allowed to use F* ghost operations like ``reveal : erased a -> GTot +a``, unobservable computations are not. + +A ``stt_ghost`` computation with a non-informative result can be +lifted to ``stt_unobservable``. + +Opening an invariant +++++++++++++++++++++ + +Now that we've allocated an ``inv (owns r)``, what can we do with it? +As we said earlier, one can make use of the ``owns r`` in an atomic +computation, so long as we restore it at the end of the atomic +step. + +The ``with_invariants`` construct gives us access to the invariant +within the scope of at most one atomic step, preceded or succeeded by +as many ghost or unobservable steps as needed. + +The general form of ``with_invariants`` is as follows, to "open" +invariants ``i_1`` to ``i_k`` in the scope of ``e``. + +.. code-block:: pulse + + with_invariants i_1 ... i_k + returns x:t + ensures post + { e } + + +In many cases, the ``returns`` and ``ensures`` annotations are +omitted, since it can be inferred. + +This is syntactic sugar for the following nest: + +.. code-block:: pulse + + with_invariants i_1 { + ... + with_invariants i_k + returns x:t + ensures post + { e } + ... + } + +Here's the rule for opening a single invariant ``i:inv p`` using +``with_invariant i { e }`` is as follows: + +* ``i`` must have type ``inv p``, for some ``p:vprop`` + +* ``e`` must have the type ``stt_atomic t j (p ** r) (fun x -> p ** s + x)``. [#]_ That is, ``e`` requires and restores the invariant ``p``, + while also transforming ``r`` to ``s x``, all in at most one atomic + step. Further, the ``name_of_inv i`` must not be in the set ``j``. + +* ``with_invariants i { e }`` has typ ``stt_atomic t (add_inv i j) r + s``. That is, ``e`` gets to use ``p`` for a step, and from the + caller's perspective, the context was transformed from ``r`` to + ``s``, while the use of ``p`` is hidden. + +* Pay attention to the ``add_inv i j`` index on ``with_invariants``: + ``stt_atomic`` (or ``stt_unobservable``) computation is indexed by + the names of all the invariants that it may open. + +Let's look at a few examples to see how ``with_invariants`` works. + +.. [#] + + Note, ``e`` may also have type ``stt_unobservable t j (p ** r) (fun + x -> p ** s x)``, in which case ``with_invariant i { e }`` has type + ``stt_unobservable t (add_inv i j) r s``. + + +Updating a reference +~~~~~~~~~~~~~~~~~~~~ + +In the example below, given ``inv (owns r)``, we can atomically update +a reference with a pre- and postcondition of ``emp``. + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //update_ref_atomic$ + :end-before: ``` + +* At the start of the ``with_invariants`` scope, we have ``owns r`` in + the context. + +* The ghost step ``unfold owns`` unfolds it to its definition. + +* Then, we do a single atomic action, ``write_atomic``. + +* And follow it up with a ``fold owns``, another ghost step. + +* The block within ``with_invariants i`` has type ``stt_atomic unit + emp_inames (owns r ** emp) (fun _ -> owns r ** emp)`` + +* Since we opened the invariant ``i``, the type of + ``update_ref_atomic`` records this in the ``opens (singleton i)`` + annotation; equivalently, the type is ``stt_atomic unit + (singleton i) emp (fun _ -> emp)``. When the ``opens`` annotation is + omitted, it defaults to ``emp_inames``, the empty set of invariant + names. + +Double opening is unsound +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To see why we have to track the names of the opened invariants, +consider the example below. If we opened the same invariant twice +within the same scope, then it's easy to prove ``False``: + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //double_open_bad$ + :end-before: //double_open_bad$ + +Here, we open the invariants ``i`` twice and get ``owns r ** owns r``, +or more than full permission to ``r``---from this, it is easy to build +a contradiction. + + +Subsuming atomic computations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Atomic computations can be silently converted to regular, ``stt`` +computations, while forgetting which invariants they opened. For +example, ``update_ref`` below is not marked atomic, so its type +doesn't record which invariants were opened internally. + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //update_ref$ + :end-before: ``` + +This is okay, since a non-atomic computation can never appear within a +``with_invariants`` block---so, there's no fear of an ``stt`` +computation causing an unsound double opening. Attempting to use a +non-atomic computation in a ``with_invariants`` block produces an +error, as shown below. + + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //update_ref_fail$ + :end-before: //update_ref_fail$ + +.. code-block:: + + - This computation is not atomic nor ghost. `with_invariants` + blocks can only contain atomic computations. diff --git a/book/pulse/pulse_higher_order.rst b/book/pulse/pulse_higher_order.rst index 3b690590ab3..6ac87d0f1fc 100755 --- a/book/pulse/pulse_higher_order.rst +++ b/book/pulse/pulse_higher_order.rst @@ -68,6 +68,33 @@ have: * the precondition is ``pre x`` and the postcondition is ``fun y -> post x y``. +Universes ++++++++++ + +For completeness, the signature of ``stt`` and ``stt_ghost`` are shown +below: + +.. code-block:: fstar + + val stt (a:Type u#a) (i:inames) (pre:vprop) (post: a -> vprop) + : Type u#0 + + val stt_ghost (a:Type u#a) (i:inames) (pre:vprop) (post: a -> vprop) + : Type u#2 + +A point to note is that ``stt`` computations live in universe +``u#0``. This is because ``stt`` computations are allowed to +infinitely loop, and are built upon :ref:`the effect of divergence +`, or ``Div``, which, as we learned earlier, lives in +universe ``u#0``. The universe of ``stt`` means that one can store an +``stt`` function in an reference, e.g., ``ref (unit -> stt unit p q)`` +is a legal type in Pulse. + +In contrast, ``stt_ghost`` functions are total and live in +universe 2. You cannot store a ``stt_ghost`` function in the state, +since that would allow writing non-terminating functions in +``stt_ghost``. + Counters ........ diff --git a/book/pulse/pulse_parallel_increment.rst b/book/pulse/pulse_parallel_increment.rst new file mode 100755 index 00000000000..af998f1a726 --- /dev/null +++ b/book/pulse/pulse_parallel_increment.rst @@ -0,0 +1,4 @@ +.. _Pulse_parallel_increment: + +Parallel Increment +================== diff --git a/book/pulse/pulse_spin_lock.rst b/book/pulse/pulse_spin_lock.rst new file mode 100755 index 00000000000..0dee8101b1c --- /dev/null +++ b/book/pulse/pulse_spin_lock.rst @@ -0,0 +1,4 @@ +.. _Pulse_spin_lock: + +Spin Locks +========== From 06d9bd64e2cb54ca3fb09df36cc2b08b757f3aec Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 11 Jan 2024 00:39:47 -0800 Subject: [PATCH 27/65] spin lock; --- book/pulse/pulse_spin_lock.rst | 163 +++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/book/pulse/pulse_spin_lock.rst b/book/pulse/pulse_spin_lock.rst index 0dee8101b1c..bf66ce336a9 100755 --- a/book/pulse/pulse_spin_lock.rst +++ b/book/pulse/pulse_spin_lock.rst @@ -2,3 +2,166 @@ Spin Locks ========== + +With atomic operations and invariants, we can build many useful +abstractions for concurrency programming. In this chapter, we'll look +at how to build a spin lock for mutual exclusion. + +Representing a Lock +................... + +The main idea of the implementation is to represent a lock using a +mutable machine word, where the value ``0ul`` signifies that the lock +is currently released; and ``1ul`` signifies the the lock is currently +acquired. To acquire a lock, we'll try to atomically compare-and-swap, +repeating until we succeed in setting a ``1ul`` and acquiring the +lock. Releasing the lock is simpler: we'll just set it to ``0ul`` +(though we'll explore a subtlety on how to handle double releases). + +From a specification perspective, a lock is lot like an invariant: the +type ``lock p`` is a kind of token that states that the lock protects +some property ``p``. Acquiring the lock provides ``p`` to the caller; +while releasing the lock requires the caller to give up ownerhip of +``p``. The runtime mutual exclusion is enforced by the acquire +spinning, or looping, until the lock is available. + +We'll represent a lock as a pair of reference to a ``U32.t`` and an +invariant: + +.. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst + :language: fstar + :start-after: //lock$ + :end-before: //lock$ + +The invariant states: + + * We hold full permission to the ``r:ref U32``; and + + * If ``r`` contains ``0ul``, then we also have ``p``. + +Creating a lock +............... + +To create a lock, we implement ``new_lock`` below. It requires the +caller to provide ``p``, ceding ownership of ``p`` to the newly +allocated ``l:lock p`` + +.. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst + :language: pulse + :start-after: //new_lock$ + :end-before: ``` + +Some notes on the implementation: + +* We heap allocate a reference using ``Box.alloc``, since the clearly, + the lock has to live beyond the scope of this function's activation. + +* We use ``new_invariant`` to create an ``inv (lock_inv _)``, and + package it up with the newly allocated reference. + +Acquiring a lock +................ + +The signature of ``acquire`` is shown below: it's says that with +``l:lock p``, we can get back ``p`` without proving anything, i.e., +the precondition is ``emp``. + +.. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst + :language: pulse + :start-after: //acquire_sig$ + :end-before: //acquire_sig$ + +This may be seem surprising at first. But, recall that we've stashed +``p`` inside the invariant stored in the lock, and ``acquire`` is +going to keep looping until such time as a CAS on the reference in the +lock succeeds, allowing us to pull out ``p`` and return it to the +caller. + +The type of a compare-and-swap is shown below, from +Pulse.Lib.Reference: + +.. code-block:: fstar + + let cond b (p q:vprop) = if b then p else q + +.. code-block:: pulse + + atomic + fn cas (r:ref U32.t) (u v:U32.t) (#i:erased U32.t) + requires pts_to r i + returns b:bool + ensures cond b (pts_to r v ** pure (reveal i == u)) + (pts_to r i)) + + +The specification of ``cas r u v`` says that we can try to atomically +update ``r`` from ``u`` to ``v``, and if the operation succeeds, we +learn that the initial value (``i``) of ``r`` was equal to ``u``. + +Using ``cas``, we can implemented ``acquire`` using a tail-recursive +function: + +.. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst + :language: pulse + :start-after: //acquire_body$ + :end-before: ``` + +The main part of the implementation is the ``with_invariants`` block. + +* Its return type ``b:bool`` and postcondition is ``maybe b p``, + signifying that after a single ``cas``, we may have ``p`` if the + ``cas`` succeeded. + +* We open ``l.i`` to get ``lock_inv``, and then try a ``cas l.r 0ul 1ul`` + +* If the ``cas`` succeeds, we know that the lock was initially in the + ``0ul`` state. So, from ``lock_inv`` we have ``p``, and we can "take + it out" of the lock and return it out of block as ``maybe true + p``. And, importantly, we can trivially restore the ``lock_inv``, + since we know its currently value is ``1ul``, i.e., ``maybe (1ul = + 0ul) _ == emp``. + +* If the ``cas`` fails, we just restore ``lock_inv`` and return false. + +Outside the ``with_invariants`` block, if the cas succeeded, then +we're done: we have ``p`` to return to the caller. Otherwise, we +recurse and try again. + +Exercise +........ + +Rewrite the tail-recursive ``acquire`` using a while loop. + +Releasing a lock +................ + +Releasing a lock is somewhat easier, at least for a simple version. +The signature is the dual of ``acquire``: the caller has to give up +``p`` to the lock. + +.. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst + :language: pulse + :start-after: //release$ + :end-before: ``` + +In this implementation, ``release`` unconditionally sets the reference +to ``0ul`` and reproves the ``lock_inv``, since we have ``p`` in +context. + +However, if the lock was already in the released state, it may already +hold ``p``---releasing an already released lock can allow the caller +to leak resources. + +Exercise +........ + +Rewrite ``release`` to spin until the lock is acquired, before +releasing it. This is not a particularly realistic design for avoiding +a double release, but it's a useful exercise. + +Exercise +........ + +Redesign the lock API to prevent double releases. One way to do this +is when acquiring to lock to give out a permission to release it, and +for ``release`` to require and consume that permission. From 288ed536c7bb2cbd95185951786569048afe6789 Mon Sep 17 00:00:00 2001 From: Aseem Rastogi Date: Thu, 11 Jan 2024 16:32:48 +0000 Subject: [PATCH 28/65] more on extraction --- book/pulse/pulse_extraction.rst | 673 ++++++++++++++++++++------------ 1 file changed, 433 insertions(+), 240 deletions(-) diff --git a/book/pulse/pulse_extraction.rst b/book/pulse/pulse_extraction.rst index 098b4316295..5a3173cddb1 100644 --- a/book/pulse/pulse_extraction.rst +++ b/book/pulse/pulse_extraction.rst @@ -3,26 +3,16 @@ Extraction =========== -Pulse programs can be extracted to OCaml, C, and Rust. +Pulse programs can be extracted to OCaml, C, and Rust. We illustrate the extraction capabilities +with the help of the `Boyer-Moore majority vote algorithm `_ +implemented in Pulse. -Rust extraction -^^^^^^^^^^^^^^^^ - -.. note:: - The Rust extraction pipeline is under heavy development. +Boyer-Moore majority vote algorithm +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -We illustrate Rust extraction with the -`Boyer-Moore majority vote algorithm `_ implemented -in Pulse. The algorithm finds majority vote in an array of votes in linear time +The algorithm finds majority vote in an array of votes in linear time (2n comparisons, where n is the length of the array) and constant extra memory. -The algorithm consists of two phases. The first phase, called the pairing phase, -pairs off disagreeing votes (cancels them) until the remaining votes are all same. -The main idea of the algorithm is to do this pairing with n comparisons. After the -pairing phase, the remaining vote must be the majority, *if the majority exists*. -The second phase, called the counting phase, checks if the remaining vote is indeed -in majority with n more comparisons. - We implement the algorithm in Pulse with the following specification: .. literalinclude:: ../code/pulse/PulseTutorial.Algorithms.fst @@ -30,40 +20,82 @@ We implement the algorithm in Pulse with the following specification: :start-after: //majorityspec$ :end-before: //majorityspecend$ -The precondition ``SZ.fits (2 * SZ.v len)`` ensures safe arithmetic in the counting -phase of the algorithm. The implementation of the function contains two while loops -for the two phases. +The precondition ``SZ.fits (2 * SZ.v len)`` ensures safe arithmetic when counting +for majority. + +The algorithm consists of two phases. The first phase, called the pairing phase, +pairs off disagreeing votes (cancels them) until the remaining votes are all same. +The main idea of the algorithm is to do this pairing with n comparisons. After the +pairing phase, the remaining vote must be the majority, *if the majority exists*. +The second phase, called the counting phase, checks if the remaining vote is indeed +in majority with n more comparisons. For the first phase, the algorithm maintains three auxiliary variables, ``i`` for the -loop counter, ``cand`` the current majority candidate, and a count ``k``. For the ``i``-th -element of the array, if ``k = 0``, the algorithm assigns the ``i``-th vote as the new -majority candidate and assigns ``k = ``. Otherwise, if the ``i``-th vote is same as +loop counter, ``cand`` the current majority candidate, and a count ``k``. It visits the +votes in a loop, where for the ``i-th`` +element of the array, if ``k = 0``, the algorithm assigns the ``i-th`` vote as the new +majority candidate and assigns ``k = 1``. Otherwise, if the ``i-th`` vote is same as ``cand``, it increments ``k`` by one, otherwise it decrements ``k`` by one. +The second phase then is another while loop that counts the number of votes +for the majority candidate from the first phase. .. literalinclude:: ../code/pulse/PulseTutorial.Algorithms.fst :language: pulse :start-after: //majorityphase1$ :end-before: //majorityphase1end$ -The loop invariant specifies majority constraints *within* the prefix of the array -that the loop has visited so far. The second phase after this is a simple counting loop. -We refer the reader to the corresponding Pulse file for more details. +The loop invariant for the first phase specifies majority constraints *within* the +prefix of the array that the loop has visited so far. The second phase loop invariant +is a simple counting invariant. -To extract ``majority`` to Rust, we first invoke F* extraction pipeline with option -``--codegen Extension``. This emits a ``.ast`` file containing an internal AST -representation of the file. Pulse framework is accompanied with a rust extration tool +Pulse automatically proves the program, with an hint for the behavior of the ``count`` +function as we increment the loop counter, the following ``count_until_next`` lemma +captures the behavior, and we invoke the lemma in both the while loops: + +.. literalinclude:: ../code/pulse/PulseTutorial.Algorithms.fst + :language: pulse + :start-after: //countlemma$ + :end-before: //countlemmaend$ + +Rust extraction +^^^^^^^^^^^^^^^^ + +Pulse toolchain is accompanied with a tool to extract Pulse programs to Rust. +The extraction pipeline maps the Pulse syntactic constructs such as ``let mut``, +``while``, ``if-then-else``, etc. to corresponding Rust constructs. Further, +Pulse libraries are mapped to their Rust counterparts, e.g. ``Pulse.Lib.Vec`` to +``std::vec``, ``Pulse.Lib.Array`` to Rust slices etc. + +To extract a Pulse file to Rust, we first invoke the F* extraction pipeline with +the command line option ``--codegen Extension``. This emits a ``.ast`` file containing +an internal AST representation of the file. We then invoke the Rust extraction tool that takes as input the ``.ast`` files and outputs the extracted Rust code (by-default the output is written to ``stdout``, if an ``-o `` option is provided to the tool, -the output is written to ``file``). The output of the tool on this example is as shown -below: +the output is written to ``file``). For example, the first command produces the ``.ast`` +file from ``PulseTutorial.Algorithms.fst`` (which contains the Boyer-Moore algorithm implementation), +and then the second command extracts the Rust code to ``voting.rs``. (These commands are run in the +``steel`` root directory, change the location of main.exe according to your setup.) + +.. code-block:: shell + + $ fstar.exe --include lib/steel/ --include lib/steel/pulse --include share/steel/examples/pulse/lib/ + --include share/steel/examples/pulse/by-example/ --include share/steel/examples/pulse/_output/cache/ + --load_cmxs steel --odir . PulseTutorial.Algorithms.fst + --extract 'PulseTutorial.Algorithms' --codegen Extension + + $ ./pulse2rust/main.exe PulseTutorial_Algorithms.ast -o voting.rs + +The output Rust code is as shown below: .. literalinclude:: ../code/pulse/voting.rs :language: pulse :start-after: //majorityrust$ :end-before: //majorityrustend$ -We can output this code in a file, and then test it as follows: +We can test it by adding the following in ``voting.rs`` and running the tests +(using ``cargo test``, it requires a ``Cargo.toml`` file, we provide an example file +in the repo that can be used): .. literalinclude:: ../code/pulse/voting.rs :language: pulse @@ -85,214 +117,375 @@ A few notes about the extracted Rust code: pipeline currently passes all the references as ``mut``, we plan to make it more precise by taking into account the permissions from the Pulse signature. +C extraction +^^^^^^^^^^^^^ + +Pulse programs can also be extracted to C. The extraction pipeline is based on the +`Karamel `_ tool. The process to extract Pulse +programs to C is similar to that of extracting Low* to C, described in +`this tutorial `_. In summary, we first generate +``.krml`` files from using the F* extraction command line option ``--codegen krml``, and then +run the Karamel tool on those files. + +One catch with extracting our Boyre-Moore implementation to C is that due to the lack +of support of polymorphism in C, Karamel monomorphicizes polymorphic functions based on +their uses. So, we write a monomorphic version of the ``majority`` function for ``u32``, +that internally calls the polymorphic ``majority`` function: + +.. literalinclude:: ../code/pulse/PulseTutorial.Algorithms.fst + :language: pulse + :start-after: //majoritymono$ + :end-before: //majoritymonoend$ + +Then we extract it to C as follows (the commands are run in the ``steel`` root directory as before): + +.. code-block:: shell + + $ fstar.exe --include lib/steel/ --include lib/steel/pulse --include share/steel/examples/pulse/lib/ + --include share/steel/examples/pulse/by-example/ --include share/steel/examples/pulse/_output/cache/ + --load_cmxs steel --odir . PulseTutorial.Algorithms.fst + --extract 'FStar.Pervasives.Native PulseTutorial.Algorithms' --codegen krml + + $ ../karamel/krml -skip-compilation out.krml + +This produces ``PulseTutorial_Algorithms.h`` and ``PulseTutorial_Algorithms.c`` files, with the following +implementation of ``majority``: + +.. literalinclude:: ../code/pulse/PulseTutorial_Algorithms.c + :language: C + :start-after: //majorityc$ + :end-before: //majoritycend$ + +We can now test it with a client like: + +.. literalinclude:: ../code/pulse/PulseTutorial_Algorithms_Client.c + :language: C + + +.. code-block:: shell + + $ gcc PulseTutorial_Algorithms.c PulseTutorial_Algorithms_Client.c -I ../karamel/include/ + -I ../karamel/krmllib/c -I ../karamel/krmllib/dist/minimal/ + + $ ./a.out + Majority: 1 + + $ + +OCaml extraction +^^^^^^^^^^^^^^^^^ + +As with all F* programs, Pulse programs can be extracted to OCaml. One caveat +with using the OCaml backend for Pulse programs is that the explicit memory +management from Pulse programs does not carry over to OCaml. For example, the +extracted OCaml programs rely on the OCaml garbage collector for reclaiming unused +heap memory, ``let mut`` variables are allocated on the heap, etc. + +For the Boyer-Moore example, we can extract the program to OCaml as follows: + +.. code-block:: shell + + $ fstar.exe --include lib/steel/ --include lib/steel/pulse --include share/steel/examples/pulse/lib/ + --include share/steel/examples/pulse/by-example/ --include share/steel/examples/pulse/_output/cache/ + --load_cmxs steel --odir . PulseTutorial.Algorithms.fst + --extract 'PulseTutorial.Algorithms' --codegen OCaml + +and the extracted ``majority`` function looks like: + +.. literalinclude:: ../code/pulse/PulseTutorial_Algorithms.ml + :language: C + :start-after: //majorityocaml$ + :end-before: //majorityocamlend$ + + +.. Rust extraction +.. ^^^^^^^^^^^^^^^^ + +.. .. note:: +.. The Rust extraction pipeline is under heavy development. + +.. We illustrate Rust extraction with the +.. `Boyer-Moore majority vote algorithm `_ implemented +.. in Pulse. The algorithm finds majority vote in an array of votes in linear time +.. (2n comparisons, where n is the length of the array) and constant extra memory. + +.. The algorithm consists of two phases. The first phase, called the pairing phase, +.. pairs off disagreeing votes (cancels them) until the remaining votes are all same. +.. The main idea of the algorithm is to do this pairing with n comparisons. After the +.. pairing phase, the remaining vote must be the majority, *if the majority exists*. +.. The second phase, called the counting phase, checks if the remaining vote is indeed +.. in majority with n more comparisons. + +.. We implement the algorithm in Pulse with the following specification: + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Algorithms.fst +.. :language: pulse +.. :start-after: //majorityspec$ +.. :end-before: //majorityspecend$ + +.. The precondition ``SZ.fits (2 * SZ.v len)`` ensures safe arithmetic in the counting +.. phase of the algorithm. The implementation of the function contains two while loops +.. for the two phases. + +.. For the first phase, the algorithm maintains three auxiliary variables, ``i`` for the +.. loop counter, ``cand`` the current majority candidate, and a count ``k``. For the ``i``-th +.. element of the array, if ``k = 0``, the algorithm assigns the ``i``-th vote as the new +.. majority candidate and assigns ``k = ``. Otherwise, if the ``i``-th vote is same as +.. ``cand``, it increments ``k`` by one, otherwise it decrements ``k`` by one. + + +.. .. literalinclude:: ../code/pulse/PulseTutorial.Algorithms.fst +.. :language: pulse +.. :start-after: //majorityphase1$ +.. :end-before: //majorityphase1end$ + +.. The loop invariant specifies majority constraints *within* the prefix of the array +.. that the loop has visited so far. The second phase after this is a simple counting loop. +.. We refer the reader to the corresponding Pulse file for more details. + +.. To extract ``majority`` to Rust, we first invoke F* extraction pipeline with option +.. ``--codegen Extension``. This emits a ``.ast`` file containing an internal AST +.. representation of the file. Pulse framework is accompanied with a rust extration tool +.. that takes as input the ``.ast`` files and outputs the extracted Rust code (by-default +.. the output is written to ``stdout``, if an ``-o `` option is provided to the tool, +.. the output is written to ``file``). The output of the tool on this example is as shown +.. below: + +.. .. literalinclude:: ../code/pulse/voting.rs +.. :language: pulse +.. :start-after: //majorityrust$ +.. :end-before: //majorityrustend$ + +.. We can output this code in a file, and then test it as follows: -.. Mutable Arrays -.. =============== - -.. In this chapter, we will learn about mutable arrays in Pulse. An array -.. is a contiguous collection of values of the same type. Similar to ``ref``, -.. arrays in Pulse can be allocated in the stack frame of the current function -.. or in the heap---while the stack allocated arrays are reclaimed automatically -.. (e.g., when the function returns), heap allocated arrays are explicitly managed -.. by the programmer. - -.. Pulse provides two array types: ``Pulse.Lib.Array.array t`` as the basic array type -.. and ``Pulse.Lib.Vec.vec t`` for heap allocated arrays. To provide code reuse, functions -.. that may operate over both stack and heap allocated arrays can be written using -.. ``Pulse.Lib.Array.array t``---the ``Pulse.Lib.Vec`` library provides back-and-forth coercions -.. between ``vec t`` and ``array t``. - -.. ``array t`` -.. ^^^^^^^^^^^^ - -.. We illustrate the basics of ``array t`` with the help of the following example -.. that reads an array: - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: ```pulse //readi$ -.. :end-before: ``` - -.. The library provides a points-to predicate ``pts_to arr #p s`` with -.. the interpretation that in the current memory, the contents of ``arr`` -.. are same as the (functional) sequence ``s:FStar.Seq.seq t``. Like the -.. ``pts_to`` predicate on reference, it is also indexed by an implicit -.. fractional permission ``p``, which distinguished shared, read-only -.. access from exclusive read/write access. - -.. In the arguments of ``read_i``, the argument ```s`` is erased, since -.. it is for specification only. - -.. Arrays can be read and written-to using indexes of type -.. ``FStar.SizeT.t``, a model of C ``size_t`` [#]_ in F*, provided that -.. the index is within the array bounds---the refinement ``SZ.v i < -.. Seq.length s`` enforces that the index is in bounds, where ``module SZ -.. = FStar.SizeT``. The function returns the ``i``-th element of the -.. array, the asserted by the postcondition vprop ``pure (x == Seq.index -.. s (SZ.v i))``. The body of the function uses the array read operator -.. ``arr.(i)``. - -.. As another example, let's write to the ``i``-th element of an array: - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: ```pulse //writei$ -.. :end-before: ``` - -.. The function uses the array write operator ``arr(i) <- x`` and the postcondition -.. asserts that in the state when the function returns, the contents of the array -.. are same as the sequence ``s`` updated at the index ``i``. - -.. While any permission suffices for reading, writing requires -.. ``full_perm``. For example, implementing ``write_i`` without -.. ``full_perm`` is rejected, as shown below. - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: //writeipbegin$ -.. :end-before: //writeipend$ - -.. The library contains ``share`` and ``gather`` functions, similar to -.. those for references, to divide and combine permissions on arrays. - -.. We now look at a couple of examples that use arrays with conditionals, -.. loops, existentials, and invariants, using many of the Pulse -.. constructs we have seen so far. - -.. .. [#] ``size_t`` in C is an unsigned integer type that is at least -.. ``16`` bits wide. The upper bound of ``size_t`` is platform -.. dependent. ``FStar.SizeT.size_t`` models this type and is -.. extracted to the primitive ``size_t`` type in C, similar to the -.. other :ref:`bounded integer types ` discussed -.. previously. +.. .. literalinclude:: ../code/pulse/voting.rs +.. :language: pulse +.. :start-after: //majorityrusttest$ +.. :end-before: //majorityrusttestend$ + +.. A few notes about the extracted Rust code: + +.. - The Pulse function and the Rust function are generic in the type of the votes. In Rust, +.. the extracted code required the type argument to implement the ``Clone``, ``Copy``, and +.. ``PartialEq`` traits. Currently we hardcode these traits. We plan to specify these traits +.. in Pulse through attribute mechanism + +.. - The ghost arguments ``p`` and ``s`` appear in the Rust code as ``unit`` arguments, we plan +.. to make it so that these arguments are completely erased. + +.. - Whereas ``majority`` needs only read permission for the ``votes`` array in the Pulse +.. signature, the extracted Rust code specifies the argument as ``&mut``. The Rust extraction +.. pipeline currently passes all the references as ``mut``, we plan to make +.. it more precise by taking into account the permissions from the Pulse signature. + + +.. .. Mutable Arrays +.. .. =============== + +.. .. In this chapter, we will learn about mutable arrays in Pulse. An array +.. .. is a contiguous collection of values of the same type. Similar to ``ref``, +.. .. arrays in Pulse can be allocated in the stack frame of the current function +.. .. or in the heap---while the stack allocated arrays are reclaimed automatically +.. .. (e.g., when the function returns), heap allocated arrays are explicitly managed +.. .. by the programmer. + +.. .. Pulse provides two array types: ``Pulse.Lib.Array.array t`` as the basic array type +.. .. and ``Pulse.Lib.Vec.vec t`` for heap allocated arrays. To provide code reuse, functions +.. .. that may operate over both stack and heap allocated arrays can be written using +.. .. ``Pulse.Lib.Array.array t``---the ``Pulse.Lib.Vec`` library provides back-and-forth coercions +.. .. between ``vec t`` and ``array t``. + +.. .. ``array t`` +.. .. ^^^^^^^^^^^^ + +.. .. We illustrate the basics of ``array t`` with the help of the following example +.. .. that reads an array: + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: ```pulse //readi$ +.. .. :end-before: ``` + +.. .. The library provides a points-to predicate ``pts_to arr #p s`` with +.. .. the interpretation that in the current memory, the contents of ``arr`` +.. .. are same as the (functional) sequence ``s:FStar.Seq.seq t``. Like the +.. .. ``pts_to`` predicate on reference, it is also indexed by an implicit +.. .. fractional permission ``p``, which distinguished shared, read-only +.. .. access from exclusive read/write access. + +.. .. In the arguments of ``read_i``, the argument ```s`` is erased, since +.. .. it is for specification only. + +.. .. Arrays can be read and written-to using indexes of type +.. .. ``FStar.SizeT.t``, a model of C ``size_t`` [#]_ in F*, provided that +.. .. the index is within the array bounds---the refinement ``SZ.v i < +.. .. Seq.length s`` enforces that the index is in bounds, where ``module SZ +.. .. = FStar.SizeT``. The function returns the ``i``-th element of the +.. .. array, the asserted by the postcondition vprop ``pure (x == Seq.index +.. .. s (SZ.v i))``. The body of the function uses the array read operator +.. .. ``arr.(i)``. + +.. .. As another example, let's write to the ``i``-th element of an array: + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: ```pulse //writei$ +.. .. :end-before: ``` + +.. .. The function uses the array write operator ``arr(i) <- x`` and the postcondition +.. .. asserts that in the state when the function returns, the contents of the array +.. .. are same as the sequence ``s`` updated at the index ``i``. + +.. .. While any permission suffices for reading, writing requires +.. .. ``full_perm``. For example, implementing ``write_i`` without +.. .. ``full_perm`` is rejected, as shown below. + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: //writeipbegin$ +.. .. :end-before: //writeipend$ + +.. .. The library contains ``share`` and ``gather`` functions, similar to +.. .. those for references, to divide and combine permissions on arrays. + +.. .. We now look at a couple of examples that use arrays with conditionals, +.. .. loops, existentials, and invariants, using many of the Pulse +.. .. constructs we have seen so far. + +.. .. .. [#] ``size_t`` in C is an unsigned integer type that is at least +.. .. ``16`` bits wide. The upper bound of ``size_t`` is platform +.. .. dependent. ``FStar.SizeT.size_t`` models this type and is +.. .. extracted to the primitive ``size_t`` type in C, similar to the +.. .. other :ref:`bounded integer types ` discussed +.. .. previously. -.. Compare -.. ........ - -.. Let's implement a function that compares two arrays for equality: - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: //comparesigbegin$ -.. :end-before: //comparesigend$ - -.. The function takes two arrays ``a1`` and ``a2`` as input, and returns a boolean. -.. The postcondition ``pure (res <==> Seq.equal 's1 's2)`` -.. specifies that the boolean is true if and only if the sequence representations of the -.. two arrays are equal. Since the function only reads the arrays, it is parametric in the -.. permissions ``p1`` and ``p2`` on the two arrays. Note that the type parameter ``t`` has -.. type :ref:`eqtype`, requiring that values of type ``t`` support -.. decidable equality. - -.. One way to implement ``compare`` is to use a ``while`` loop, reading the two arrays -.. using a mutable counter and checking that the corresponding elements are equal. - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: //compareimplbegin$ -.. :end-before: //compareimplend$ - -.. The loop invariant states that (a) the arrays are pointwise equal up to the current value -.. of the counter, and (b) the boolean ``b`` is true if and only if the current value -.. of the counter is less than the length of the arrays and the arrays are equal at that index. -.. While (a) helps proving the final postcondition of ``compare``, (b) is required to maintain the -.. invariant after the counter is incremented in the loop body. - -.. Copy -.. ..... - -.. As our next example, let's implement a ``copy`` function that copies the contents -.. of the array ``a2`` to ``a1``. - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: //copy$ -.. :end-before: ``` - -.. The loop invariant existentially abstracts over the contents of ``a1``, and maintains -.. that upto the current loop counter, the contents of the two arrays are equal. Rest of -.. the code is straightforward, the loop conditional checks that the loop counter is less -.. than the array lengths and the loop body copies one element at a time. - -.. The reader will notice that the postcondition of ``copy`` is a little convoluted. -.. A better signature would be the following, where we directly state that the -.. contents of ``a1`` are same as ``'s2``: - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: //copy2sigbegin$ -.. :end-before: //copy2sigend$ - -.. We can implement this signature, but it requires one step of rewriting at the end -.. after the ``while`` loop to get the postcondition in this exact shape: - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: //copy2rewriting$ -.. :end-before: //copy2rewritingend$ - -.. We could also rewrite the predicates explicitly, as we saw in a -.. :ref:`previous chapter `. - - -.. Stack allocated arrays -.. ^^^^^^^^^^^^^^^^^^^^^^^ - -.. Stack arrays can be allocated using the expression ``[| v; n |]``. It -.. allocates an array of size ``n``, with all the array elements -.. initialized to ``v``. The size ``n`` must be compile-time constant. -.. It provides the postcondition that the newly create array points to a -.. length ``n`` sequence of ``v``. The following example allocates two -.. arrays on the stack and compares them using the ``compare`` function -.. above. - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: ```pulse //compare_stack_arrays$ -.. :end-before: ``` - -.. As with the stack references, stack arrays don't need to be deallocated or -.. dropped, they are reclaimed automatically when the function returns. As a result, -.. returning them from the function is not allowed: - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: //ret_stack_array$ -.. :end-before: //ret_stack_array_end$ - -.. Heap allocated arrays -.. ^^^^^^^^^^^^^^^^^^^^^^ - -.. The library ``Pulse.Lib.Vec`` provides the type ``vec t``, for -.. heap-allocated arrays: ``vec`` is to ``array`` as ``box`` is to -.. ``ref``. - -.. Similar to ``array``, ``vec`` is accompanied with a ``pts_to`` -.. assertion with support for fractional permissions, ``share`` and -.. ``gather`` for dividing and combining permissions, and read and write -.. functions. However, unlike ``array``, the ``Vec`` library provides -.. allocation and free functions. - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: //heaparray$ -.. :end-before: //heaparrayend$ - -.. As with the heap references, heap allocated arrays can be coerced to ``array`` using the coercion -.. ``vec_to_array``. To use the coercion, it is often required to convert ``Vec.pts_to`` to ``Array.pts_to`` -.. back-and-forth; the library provides ``to_array_pts_to`` and ``to_vec_pts_to`` lemmas for this purpose. - -.. The following example illustrates the pattern. It copies the contents of a stack array into a heap array, -.. using the ``copy2`` function we wrote above. - -.. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst -.. :language: pulse -.. :start-after: ```pulse //copyuse$ -.. :end-before: ``` - -.. Note how the assertion for ``v`` transforms from ``V.pts_to`` to ``pts_to`` (the points-to assertion -.. for arrays) and back. It means that array algorithms and routines can be implemented with the -.. ``array t`` type, and then can be reused for both stack- and heap-allocated arrays. - -.. Finally, though the name ``vec a`` evokes the Rust ``std::Vec`` library, we don't yet support automatic -.. resizing. +.. .. Compare +.. .. ........ + +.. .. Let's implement a function that compares two arrays for equality: + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: //comparesigbegin$ +.. .. :end-before: //comparesigend$ + +.. .. The function takes two arrays ``a1`` and ``a2`` as input, and returns a boolean. +.. .. The postcondition ``pure (res <==> Seq.equal 's1 's2)`` +.. .. specifies that the boolean is true if and only if the sequence representations of the +.. .. two arrays are equal. Since the function only reads the arrays, it is parametric in the +.. .. permissions ``p1`` and ``p2`` on the two arrays. Note that the type parameter ``t`` has +.. .. type :ref:`eqtype`, requiring that values of type ``t`` support +.. .. decidable equality. + +.. .. One way to implement ``compare`` is to use a ``while`` loop, reading the two arrays +.. .. using a mutable counter and checking that the corresponding elements are equal. + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: //compareimplbegin$ +.. .. :end-before: //compareimplend$ + +.. .. The loop invariant states that (a) the arrays are pointwise equal up to the current value +.. .. of the counter, and (b) the boolean ``b`` is true if and only if the current value +.. .. of the counter is less than the length of the arrays and the arrays are equal at that index. +.. .. While (a) helps proving the final postcondition of ``compare``, (b) is required to maintain the +.. .. invariant after the counter is incremented in the loop body. + +.. .. Copy +.. .. ..... + +.. .. As our next example, let's implement a ``copy`` function that copies the contents +.. .. of the array ``a2`` to ``a1``. + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: //copy$ +.. .. :end-before: ``` + +.. .. The loop invariant existentially abstracts over the contents of ``a1``, and maintains +.. .. that upto the current loop counter, the contents of the two arrays are equal. Rest of +.. .. the code is straightforward, the loop conditional checks that the loop counter is less +.. .. than the array lengths and the loop body copies one element at a time. + +.. .. The reader will notice that the postcondition of ``copy`` is a little convoluted. +.. .. A better signature would be the following, where we directly state that the +.. .. contents of ``a1`` are same as ``'s2``: + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: //copy2sigbegin$ +.. .. :end-before: //copy2sigend$ + +.. .. We can implement this signature, but it requires one step of rewriting at the end +.. .. after the ``while`` loop to get the postcondition in this exact shape: + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: //copy2rewriting$ +.. .. :end-before: //copy2rewritingend$ + +.. .. We could also rewrite the predicates explicitly, as we saw in a +.. .. :ref:`previous chapter `. + + +.. .. Stack allocated arrays +.. .. ^^^^^^^^^^^^^^^^^^^^^^^ + +.. .. Stack arrays can be allocated using the expression ``[| v; n |]``. It +.. .. allocates an array of size ``n``, with all the array elements +.. .. initialized to ``v``. The size ``n`` must be compile-time constant. +.. .. It provides the postcondition that the newly create array points to a +.. .. length ``n`` sequence of ``v``. The following example allocates two +.. .. arrays on the stack and compares them using the ``compare`` function +.. .. above. + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: ```pulse //compare_stack_arrays$ +.. .. :end-before: ``` + +.. .. As with the stack references, stack arrays don't need to be deallocated or +.. .. dropped, they are reclaimed automatically when the function returns. As a result, +.. .. returning them from the function is not allowed: + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: //ret_stack_array$ +.. .. :end-before: //ret_stack_array_end$ + +.. .. Heap allocated arrays +.. .. ^^^^^^^^^^^^^^^^^^^^^^ + +.. .. The library ``Pulse.Lib.Vec`` provides the type ``vec t``, for +.. .. heap-allocated arrays: ``vec`` is to ``array`` as ``box`` is to +.. .. ``ref``. + +.. .. Similar to ``array``, ``vec`` is accompanied with a ``pts_to`` +.. .. assertion with support for fractional permissions, ``share`` and +.. .. ``gather`` for dividing and combining permissions, and read and write +.. .. functions. However, unlike ``array``, the ``Vec`` library provides +.. .. allocation and free functions. + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: //heaparray$ +.. .. :end-before: //heaparrayend$ + +.. .. As with the heap references, heap allocated arrays can be coerced to ``array`` using the coercion +.. .. ``vec_to_array``. To use the coercion, it is often required to convert ``Vec.pts_to`` to ``Array.pts_to`` +.. .. back-and-forth; the library provides ``to_array_pts_to`` and ``to_vec_pts_to`` lemmas for this purpose. + +.. .. The following example illustrates the pattern. It copies the contents of a stack array into a heap array, +.. .. using the ``copy2`` function we wrote above. + +.. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst +.. .. :language: pulse +.. .. :start-after: ```pulse //copyuse$ +.. .. :end-before: ``` + +.. .. Note how the assertion for ``v`` transforms from ``V.pts_to`` to ``pts_to`` (the points-to assertion +.. .. for arrays) and back. It means that array algorithms and routines can be implemented with the +.. .. ``array t`` type, and then can be reused for both stack- and heap-allocated arrays. + +.. .. Finally, though the name ``vec a`` evokes the Rust ``std::Vec`` library, we don't yet support automatic +.. .. resizing. From 454dd299aaefb1a38a25613c318f193bd456755c Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 12 Jan 2024 17:05:46 -0800 Subject: [PATCH 29/65] more on parallel increment --- book/fstar_pygments.py | 3 +- book/pulse/pulse_ch2.rst | 6 +- book/pulse/pulse_parallel_increment.rst | 299 ++++++++++++++++++++++++ 3 files changed, 304 insertions(+), 4 deletions(-) diff --git a/book/fstar_pygments.py b/book/fstar_pygments.py index 87262fc06e8..e248f5dd411 100644 --- a/book/fstar_pygments.py +++ b/book/fstar_pygments.py @@ -106,7 +106,8 @@ class CustomLexer(RegexLexer): "while", "invariant", "with_invariants", - "opens" + "opens", + "parallel" ) class PulseLexer(RegexLexer): diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index a1aa861dbf0..2b3b846680a 100644 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -61,9 +61,9 @@ is an erased value witnessing the current contents referenced by :end-before: ``` Notice how the precondition requires ``pts_to r w`` while the -postcondition changes to ``pts_to r v``, along with the property that -``v == reveal w``, i.e., the type proves that if we read the reference -the value we get is equal to the logical witness provided. +postcondition retains ``pts_to r w``, along with the property that ``v +== reveal w``, i.e., the type proves that if we read the reference the +value we get is equal to the logical witness provided. Erased values are for specification and proof only diff --git a/book/pulse/pulse_parallel_increment.rst b/book/pulse/pulse_parallel_increment.rst index af998f1a726..aed938b5d1b 100755 --- a/book/pulse/pulse_parallel_increment.rst +++ b/book/pulse/pulse_parallel_increment.rst @@ -2,3 +2,302 @@ Parallel Increment ================== + +In this chapter, we look at an example first studied by Susan Owicki +and David Gries, in a classic paper titled `Verifying properties of +parallel programs: an axiomatic approach +`_. The problem involves +proving that a program that atomically increments an integer reference +``r`` twice in parallel correctly adds 2 to ``r``. There are many ways +to do this---Owicki & Gries' approach, adapted to a modern separation +logic, involves the use of additional ghost state and offers a modular +way to structure the proof. + +While this is a very simple program, it captures the essence of some +of the reasoning challenges posed by concurrency: two threads interact +with a shared resource, contributing to it in an undetermined order, +and one aims to reason about the overall behavior, ideally without +resorting to directly analyzing each of the possible interleavings. + +Parallel Blocks +............... + +Pulse provides a few primitives for creating new threads. The most +basic one is parallel composition, as shown below: + +.. code-block:: pulse + + parallel + requires p1 and p2 + ensures q1 and q2 + { e1 } + { e2 } + +The typing rule for this construct requires: + +.. code-block:: pulse + + val e1 : stt a p1 q1 + val e2 : stt b p2 q2 + +and the ``parallel`` block then has the type: + +.. code-block:: pulse + + stt (a & b) (p1 ** p2) (fun (x, y) -> q1 x ** q2 y) + +In other words, if the current context can be split into separate +parts ``p1`` and ``p2`` satisfying the preconditions of ``e1`` and +``e2``, then the parallel block executes ``e1`` and ``e2`` in +parallel, waits for both of them to finish, and if they both do, +returns their results as a pair, with their postconditions on each +component. + +Using ``parallel``, one can easily program the ``par`` combinator +below: + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: //par$ + :end-before: ``` + +As we saw in the :ref:`introduction to Pulse `, it's easy +to increment two separate references in parallel: + +.. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst + :language: pulse + :start-after: ```pulse //par_incr + :end-before: ``` + +But, what if we wanted to increment the same reference in two separate +threads? That is, we wanted to program something like this: + +.. code-block:: pulse + + fn add2 (x:ref int) + requires pts_to x 'i + ensures pts_to x ('i + 2) + { + par (fun _ -> incr x) + (fun _ -> incr x) + } + +But, this program doesn't check. The problem is that we have only a +single ``pts_to x 'i``, and we can't split it to share among the +threads, since both threads require full permission to ``x`` to update +it. + +Further, for the program to correctly add ``2`` to ``x``, each +increment operations must take place atomically, e.g., if the two +fragments below were executed in parallel, then they may both read the +initial value of ``x`` first, bind it to ```v``, and then both update +it to ``v + 1``. + +.. code-block:: pulse + + let v = !x; || let v = !x; + x := v + 1; || x := v + 1; + +Worse, without any synchronization, on modern processors with weak +memory models, this program could exhibit a variety of other +behaviors. + +To enforce synchronization, we could use a lock, e.g., shown below: + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: ```pulse //attempt$ + :end-before: ``` + +This program is type correct and free from data races. But, since the +lock holds the entire permission on ``x``, there's no way to give this +function a meaningful postcondition, aside from ``emp``. + +A First Take, with Locks +........................ + +Owicki and Gries' idea was to augment the program with axuiliary +variables, or ghost state, that are purely for specification +purposes. Each thread gets its own piece of ghost state, and accounts +for how much that thread has contributed to the current value of +shared variable. Let's see how this works in Pulse. + +The main idea is captured by ``lock_inv``, the type of the predicate +protected by the lock: + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: fstar + :start-after: //lock_inv$ + :end-before: //lock_inv$ + +Our strawman ``lock`` in the ``attempt`` shown before had type ``lock +(exists* v. pts_to x v)``. This time, we add a conjunct that refines +the value ``v``, i.e., the predicate ``contributions l r init v`` says +that the current value of ``x`` protected by the lock (i.e., ``v``) is +equal to ``init + vl + vr``, where ``init`` is the initial value of +``x``; ``vl`` is the value of the ghost state owned by the "left" +thread; and ``vr`` is the value of the ghost state owned by the +"right" thread. In other words, the predicate ``contributions l r init +v`` shows that ``v`` always reflects the values of the contributions +made by each thread. + +Note, however, the ``contributions`` predicate only holds +half-permission on the left and right ghost variables. The other half +permission is held outside the lock and allows us to keep track of +each threads contribution in our specifications. + +Here's the code for the left thread, ``incr_left``: + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: ```pulse //incr_left$ + :end-before: ``` + +* Its arguments include ``x`` and the ``lock``, but also both pieces + of ghost state, ``left`` and ``right``, and an erased value ``i`` + for the initial value of ``x``. + +* Its precondition holds half permission on the ghost reference + ``left`` + +* Its postcondition returns half-permission to ``left``, but proves + that it was incremented, i.e., the contribution of the left thread + to the value of ``x`` increased by ``1``. + +Notice that even though we only had half permission to ``left``, the +specifications says we have updated ``left``---that's because we can +get the other half permission we need by acquiring the lock. + +* We acquire the lock and update increment the value stored in ``x``. + +* And then we follow the increment with several ghost steps: + + - Gain full permission on ``left`` by combining the half permission + from the precondition with the half permission gained from the + lock. + + - Increment ``left``. + + - Share it again, returning half permission to the lock when we + release it. + +* Finally, we ``GR.pts_to left #one_half (`vl + 1)`` left over to + return to the caller in the postcondition. + +The code of the right thread is symmetrical, but in this, our first +take, we have to essentially repeat the code---we'll see how to remedy +this shortly. + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: ```pulse //incr_right$ + :end-before: ``` + +Finally, we can implement ``add2`` with the specification we want: + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: ```pulse //add2$ + :end-before: ``` + +* We allocate ``left`` and ``right`` ghost references, initializing + them to ``0``. + +* Then we split them, putting half permission to both in the lock, + retaining the other half. + +* Then spawn two threads for ``incr_left`` and ``incr_right``, and get + as a postcondition that contributions of both threads and increased + by one each. + +* Finally, we acquire the lock, get ``pts_to x v``, for some ``v``, + and ``contributions left right i v``. Once we gather up the + permission on ``left`` and ``right``, and now the ``contributions + left right i v`` tells us that ``v == i + 1 + 1``, which is what we + need to conclude. + +This version also has the problem that we allocate lock and then +acquire it at the end, effectively leaking the memory associated with +the lock. We'll see how to fix that below. + + +Modularity with higher-order ghost code +....................................... + +Our next attempt aims to write a single function ``incr``, rather than +``incr_left`` and ``incr_right``, and to give ``incr`` a more +abstract, modular specification. The style we use here is based on an +idea proposed by Bart Jacobs and Frank Piessens in a paper titled +`Expressive modular fine-grained concurrency specification +`_. + +The main idea is to observe that ``incr_left`` and ``incr_right`` only +deferred by the ghost code that they execute. But, Pulse is higher +order: so, why not parameterize a single function by ``incr`` and let +the caller instantiate ``incr`` twice, with different bits of ghost +code. Also, while we're at it, why not also generalize the +specification of ``incr`` so that it works with any user-chosen +abstract predicate, rather than ``contributions`` and ``left/right`` +ghost state. Here's how: + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: ```pulse //incr$ + :end-before: ``` + +As before, ``incr`` requires ``x`` and the ``lock``, but, this time, +it is parameterized by: + +* A predicate ``refine``, which generalizes the ``contributions`` + predicate from before, and refines the value that ``x`` points to. + +* A predicate ``aspec``, an abstract specification chosen by the + caller, and serves as the main specification for ``incr``, which + transitions from ``aspec 'i`` to ``aspec ('i + 1)``. + +* And, finally, the ghost function itself, ``ghost_steps``, now + specified abstractly in terms of the relationship between ``refine``, + ``aspec`` and ``pts_to x``---it says, effectively, that once ``x`` + has been updated, the abstract predicates ``refine`` and ``aspec`` + can be updated too. + +Having generalized ``incr``, we've now shifted the work to the +caller. But, ``incr``, now verified once and for all, can be used with +many different callers just by instantiating it difference. For +example, if we wanted to do a three-way parallel increment, we could +reuse our ``incr`` as is. Whereas, our first take would have to be +completely revised, since ``incr_left`` and ``incr_right`` assume that +there are only two ghost references, not three. + +Here's one way to instantiate ``incr``, proving the same specification +as ``add2``. + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: ```pulse //add2_v2$ + :end-before: ``` + +The code is just a rearrangement of what we had before, factoring the +ghost code in ``incr_left`` and ``incr_right`` into a ghost function +``step``. When we spawn our threads, we pass in the ghost code to +either update the left or the right contribution. + +This code still has two issues: + +* The ghost ``step`` function is a bloated: we have essentially the + same code and proof twice, once in each branch of the + conditional. We can improve this by defining a custom bit of ghost + state using Pulse's support for partial commutative monoids---but + that's for another chapter. + +* We still leak the memory associated with the lock at the end---we'll + remedy that next. + +Exercise +++++++++ + +Instead of explicitly passing a ghost function, use a quantified trade. + +A version with invariants +......................... + From fbdf83c53acf367c22c3877d2f2c343fbc4289d5 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 12 Jan 2024 23:14:28 -0800 Subject: [PATCH 30/65] parallel increment with invariants --- book/pulse/pulse_parallel_increment.rst | 147 ++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/book/pulse/pulse_parallel_increment.rst b/book/pulse/pulse_parallel_increment.rst index aed938b5d1b..2c239360097 100755 --- a/book/pulse/pulse_parallel_increment.rst +++ b/book/pulse/pulse_parallel_increment.rst @@ -298,6 +298,153 @@ Exercise Instead of explicitly passing a ghost function, use a quantified trade. + A version with invariants ......................... +As a final example, in this section, we'll see how to program ``add2`` +using invariants and atomic operations, rather than locks. + +Doing this properly will require working with bounded, machine +integers, e.g., ``U32.t``, since these are the only types that support +atomic operations. However, to illustrate the main ideas, we'll assume +two atomic operations on unbounded integers---this will allow us to +not worry about possible integer overflow. We leave as an exercise the +problem of adapting this to ``U32.t``. + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: fstar + :start-after: //atomic_primitives$ + :end-before: //atomic_primitives$ + +Cancellable Invariants +++++++++++++++++++++++ + +The main idea of doing the ``add2`` proof is to use an invariant token +``i:inv p`` instead of a ``l:lock p``. Just as in our previous code, +``add2`` starts with allocating an invariant, putting +``exists* v. pts_to x v ** contribution left right i v`` in the +invariant. Then call incr twice in different threads. However, +finally, to recover ``pts_to x (v + 2)``, where previously we would +acquire the lock, with a regular invariant, we're stuck, since the +``pts_to x v`` permission is inside the invariant and we can't take it +out to return to the caller. + +An invariant token ``i:inv p`` is a witness that the property ``p`` is +true and remains true for the rest of a program's execution. But, what +if we wanted to only enforce ``p`` as an invariant for some finite +duration, and then to cancel it? This is what the library +``Pulse.Lib.CancellableInvariant`` provides. By allocating a ``i:inv +(cancellable t p)``, we get an invariant which enforces ``p`` only so +long as ``active perm t`` is also provable. Here's the relevant part +of the API: + +.. code-block:: pulse + + ghost + fn create (v:vprop) + requires v + returns t:token + ensures cancellable t v ** active full_perm t + + ghost + fn take (#p #t:_) (v:vprop) + requires cancellable t v ** active p t + ensures v ** active p t ** active full_perm t + + ghost + fn restore (#t:_) (v:vprop) + requires v ** active full_perm t + ensures cancellable t v + + fn cancel_inv (#t #v:_) (i:inv (cancellable t v)) + requires active full_perm t + ensures v + +A ``cancellable t v`` is created from a proof of ``v``, also providing +a fractional-permission indexed predicate ``active full_perm t``, +which can be shared and gathered as usual. So long as one can prove +that the token ``t`` is active, one can call ``take`` to trade a +``cancellable t v`` for ``v``, and vice-versa. Finally, with full +permission on the ``active`` predicate, ``cancel_inv`` is a utility to +stop enforcing ``v`` an invariant and to extract it from a ``i:inv +(cancellable t v)``. + +An increment operation +++++++++++++++++++++++ + +Our first step is to build an increment operation from an +``atomic_read`` and a ``cas``. Here is its specification: + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: //incr_atomic_spec$ + :end-before: //incr_atomic_body$ + +The style of specification is similar to the generic style we used +with ``incr``, except now we use cancellable invariant instead of a +lock. + +For its implementation, the main idea is to repeatedly read the +current value of ``x``, say ``v``; and then to ``cas`` in ``v+1`` if +the current value is still ``v``. + +The ``read`` function is relatively easy: + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: //incr_atomic_body_read$ + :end-before: //incr_atomic_body_read$ + +* We open the invariant ``l``; then, knowing that the invariant is + still active, we can ``take`` it; then read the value ``v``; + ``restore`` the invariant; and return ``v``. + +The main loop of ``incr_atomic`` is next, shown below: + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: //incr_atomic_body_loop$ + :end-before: //incr_atomic_body_loop$ + +The loop invariant says: + + * the invariant remains active + + * the local variable ``continue`` determines if the loop iteration + continues + + * and, so long as the loop continues, we still have ``aspec 'i``, + but when the loop ends we have ``aspec ('i + 1)`` + +The body of the loop is also interesting and consists of two atomic +operations. We first ``read`` the value of ``x`` into ``v``. Then we +open the invariant again try to ``cas`` in ``v+1``. If it succeeds, we +return ``false`` from the ``with_invariants`` block; otherwise +``true``. And, finally, outside the ``with_invariants`` block, we set +the ``continue`` variable accordingly. Recall that ``with_invariants`` +allows at most a single atomic operation, so we having done a ``cas``, +we are not allowed to also set ``continue`` inside the +``with_invariants`` block. + +``add2_v3`` ++++++++++++ + +Finally, we implement our parallel increment again, ``add2_v3``, this +time using invariants, though it has the same specification as before. + +.. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst + :language: pulse + :start-after: //add2_v3$ + :end-before: ``` + +The code too is very similar to ``add2_v2``, except instead of +allocating a lock, we allocate a cancellable invariant. And, at the +end, instead of + +Exercise +........ + +Implement ``add2`` on a ``ref U32.t``. You'll need a precondition that +``'i + 2 < pow2 32`` and also to strengthen the invariant to prove +that each increment doesn't overflow. From 8e3163333f641bc65edd1345481bad230f44ba58 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 14 Jan 2024 06:43:19 -0800 Subject: [PATCH 31/65] typos --- book/pulse/pulse_atomics_and_invariants.rst | 4 ++-- book/pulse/pulse_ch1.rst | 4 ++-- book/pulse/pulse_parallel_increment.rst | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst index ce12db5021d..74bd340b7a4 100755 --- a/book/pulse/pulse_atomics_and_invariants.rst +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -120,9 +120,9 @@ First, let's define a regular ``vprop``, ``owns x``, to mean that we hold full-permission on ``x``. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst - :language: pulse + :language: fstar :start-after: //owns$ - :end-before: ``` + :end-before: //owns$ Now, if we can currently proves ``pts_to r x`` then we can turn it into an invariant ``i:inv (owns r)``, as shown below. diff --git a/book/pulse/pulse_ch1.rst b/book/pulse/pulse_ch1.rst index 0ea34b76616..4b4f3f00878 100644 --- a/book/pulse/pulse_ch1.rst +++ b/book/pulse/pulse_ch1.rst @@ -48,7 +48,7 @@ propositions that describe properties about program resources, e.g., the heap. These propositions have the type ``vprop`` in Pulse, [#]_ and, under the covers in the SteelCore semantics of Pulse, a ``vprop = state -> prop``, where ``state`` represents the state of a program, -e.g., the conents of memory. It is useful (at least at first) to think +e.g., the contents of memory. It is useful (at least at first) to think of a ``vprop`` as a memory property, though we will eventually treat it more abstractly and use it to model many other kinds of resources. @@ -197,7 +197,7 @@ taste of the logic. many tools to make existentials convenient to use. ``exists x. p`` is valid in a state ``s`` if there is a witness ``w`` such that ``p [w/x]`` is valid in ``s``. For experts, existential - quantification is impredicate, in the sense that one can quantify + quantification is impredicative, in the sense that one can quantify over ``vprops`` themselves, i.e., ``exists* (p:vprop). q`` is allowed. diff --git a/book/pulse/pulse_parallel_increment.rst b/book/pulse/pulse_parallel_increment.rst index 2c239360097..45e15aa6d16 100755 --- a/book/pulse/pulse_parallel_increment.rst +++ b/book/pulse/pulse_parallel_increment.rst @@ -440,7 +440,8 @@ time using invariants, though it has the same specification as before. The code too is very similar to ``add2_v2``, except instead of allocating a lock, we allocate a cancellable invariant. And, at the -end, instead of +end, instead of acquiring, and leaking, the lock, we simply cancel the +invariant and we're done. Exercise ........ From e78934845f3695adeb6c7ba3f1369393944471ea Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 14 Jan 2024 07:00:09 -0800 Subject: [PATCH 32/65] spellcheck --- book/pulse/pulse_arrays.rst | 2 +- book/pulse/pulse_atomics_and_invariants.rst | 4 ++-- book/pulse/pulse_ch2.rst | 8 ++++---- book/pulse/pulse_extraction.rst | 4 ++-- book/pulse/pulse_ghost.rst | 2 +- book/pulse/pulse_higher_order.rst | 2 +- book/pulse/pulse_implication_and_forall.rst | 4 ++-- book/pulse/pulse_linked_list.rst | 7 ++++--- book/pulse/pulse_loops.rst | 8 ++++---- book/pulse/pulse_parallel_increment.rst | 2 +- book/pulse/pulse_spin_lock.rst | 2 +- 11 files changed, 23 insertions(+), 22 deletions(-) diff --git a/book/pulse/pulse_arrays.rst b/book/pulse/pulse_arrays.rst index 96456e8fc74..70396137f99 100644 --- a/book/pulse/pulse_arrays.rst +++ b/book/pulse/pulse_arrays.rst @@ -124,7 +124,7 @@ of the array ``a2`` to ``a1``. :end-before: ``` The loop invariant existentially abstracts over the contents of ``a1``, and maintains -that upto the current loop counter, the contents of the two arrays are equal. Rest of +that up to the current loop counter, the contents of the two arrays are equal. Rest of the code is straightforward, the loop conditional checks that the loop counter is less than the array lengths and the loop body copies one element at a time. diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst index 74bd340b7a4..6e65bf2afed 100755 --- a/book/pulse/pulse_atomics_and_invariants.rst +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -31,7 +31,7 @@ We've learned so far about :ref:`two kinds of Pulse computations computation type * Ghost computations, proven totally correct, and enforced to be - computatationally irrelevant with the ``stt_ghost`` computation + computationally irrelevant with the ``stt_ghost`` computation type. Pulse offers a third kind of computation, *atomic* computations, with @@ -219,7 +219,7 @@ Here's the rule for opening a single invariant ``i:inv p`` using while also transforming ``r`` to ``s x``, all in at most one atomic step. Further, the ``name_of_inv i`` must not be in the set ``j``. -* ``with_invariants i { e }`` has typ ``stt_atomic t (add_inv i j) r +* ``with_invariants i { e }`` has type ``stt_atomic t (add_inv i j) r s``. That is, ``e`` gets to use ``p`` for a step, and from the caller's perspective, the context was transformed from ``r`` to ``s``, while the use of ``p`` is hidden. diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index 2b3b846680a..a90fa63079e 100644 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -104,9 +104,9 @@ l-values and r-values and implicitly read the content of references, in Pulse (like in OCaml), references are explicitly dereferenced. [#]_ As the program below illustrates, references themselves can be passed to other functions (e.g., as in/out-parameters) while their current -values must be passed expliclity. +values must be passed explicitly. -The function ``add`` takes both a refrence ``r:ref int`` and a value +The function ``add`` takes both a reference ``r:ref int`` and a value ``n:int`` as arguments: .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst @@ -222,7 +222,7 @@ Pulse expects the results of all stateful operations to be explicitly The error points to the first occurrence of ``(!r)``. The message is admittedly cryptic and should be improved. As we'll see in a later chapter, the type ``stt _ _ _`` is the type of an unevaluated Pulse -comptuation---this error complains that ``add`` expected an argument +computation---this error complains that ``add`` expected an argument of type ``int`` but instead got an unevaluated computation. Fractional Permissions @@ -369,7 +369,7 @@ A few additional points to note here: Stack references are scoped and implicitly reclaimed .................................................... -To emphasize that stack references allocted with ``let mut`` are +To emphasize that stack references allocated with ``let mut`` are scoped, let's look at the program below that Pulse refuses to check: .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst diff --git a/book/pulse/pulse_extraction.rst b/book/pulse/pulse_extraction.rst index 5a3173cddb1..45a4feee0a0 100644 --- a/book/pulse/pulse_extraction.rst +++ b/book/pulse/pulse_extraction.rst @@ -127,8 +127,8 @@ programs to C is similar to that of extracting Low* to C, described in ``.krml`` files from using the F* extraction command line option ``--codegen krml``, and then run the Karamel tool on those files. -One catch with extracting our Boyre-Moore implementation to C is that due to the lack -of support of polymorphism in C, Karamel monomorphicizes polymorphic functions based on +One catch with extracting our Boyer-Moore implementation to C is that due to the lack +of support of polymorphism in C, Karamel monomorphizes polymorphic functions based on their uses. So, we write a monomorphic version of the ``majority`` function for ``u32``, that internally calls the polymorphic ``majority`` function: diff --git a/book/pulse/pulse_ghost.rst b/book/pulse/pulse_ghost.rst index e6c7bc7f992..e959b1511b4 100755 --- a/book/pulse/pulse_ghost.rst +++ b/book/pulse/pulse_ghost.rst @@ -135,7 +135,7 @@ Pulse ghost functions with ``emp`` or ``pure _`` pre and postconditions are not that interesting---such functions can usually be written with regular F* ghost functions. -Ghost functions are aften used as proof steps to prove equivalences +Ghost functions are often used as proof steps to prove equivalences among ``vprops``. We saw a few :ref:`examples of ghost functions before `---they are ghost since their implementations are compositions of ``ghost`` functions from the Pulse diff --git a/book/pulse/pulse_higher_order.rst b/book/pulse/pulse_higher_order.rst index 6ac87d0f1fc..9c1c311d7b7 100755 --- a/book/pulse/pulse_higher_order.rst +++ b/book/pulse/pulse_higher_order.rst @@ -55,7 +55,7 @@ functions. The type of ``f`` is similar to what we had before, but this time we have: - * compuation type tag ``stt_ghost``, indication that this function + * computation type tag ``stt_ghost``, indication that this function reads or writes ghost state only, and always terminates. * the return type is ``b x`` diff --git a/book/pulse/pulse_implication_and_forall.rst b/book/pulse/pulse_implication_and_forall.rst index 4724544836c..2b4a1cadacc 100755 --- a/book/pulse/pulse_implication_and_forall.rst +++ b/book/pulse/pulse_implication_and_forall.rst @@ -65,7 +65,7 @@ Here's a small example to see ``p @==> q`` at work. The predicate ``regain_half`` says that you can trade a half-permission ``pts_to x #one_half v`` for a full permission -``pts_to x v``. At first, this may seem counterintuitive: how can you +``pts_to x v``. At first, this may seem counter-intuitive: how can you gain a full permission from half-permission. The thing to remember is that ``p @==> q`` itself holds permissions internally. In particular, ``regain_half x v`` holds ``exists* u. pts_to x #one_half u`` @@ -86,7 +86,7 @@ it into two halves---here, we just package the second half in a ghost function that allows us to gather the permission back when we need it. In the implementation, we define an auxiliary ghost function that -corresponds to the eliminator fo ``pts_to x #one_half 'v @==> pts_to x +corresponds to the eliminator for ``pts_to x #one_half 'v @==> pts_to x 'v``---it's just a ``gather``. Then, we split ``pts_to x 'v`` into halves, call ``I.intro`` passing the eliminator, and the fold it into a ``regain_half``. All ``regain_half`` has done is to package the diff --git a/book/pulse/pulse_linked_list.rst b/book/pulse/pulse_linked_list.rst index 72bc8d6b3ba..7ddb3338060 100755 --- a/book/pulse/pulse_linked_list.rst +++ b/book/pulse/pulse_linked_list.rst @@ -1,3 +1,4 @@ + .. _Pulse_linked_list: Linked Lists @@ -60,7 +61,7 @@ Case analyzing a nullable pointer When working with a linked list, the first thing we'll do, typically, is to check whether a given ``x:llist t`` is null or not. However, the -``is_list x l`` predicate is defined by case analysing ``l:list t`` +``is_list x l`` predicate is defined by case analyzing ``l:list t`` rather than ``x:llist t``, since that is makes it possible to write the predicate by recursing on the tail of ``l``. So, below, we have a predicate ``is_list_cases x l`` that inverts ``is_list x l`` predicate @@ -199,7 +200,7 @@ can get back the original permission on ``x``. +++++++++++++++ The code below shows our iterative implementation of ``length``. The -basic idea is simple , thorugh the proof takes a bit of doing. We +basic idea is simple , though the proof takes a bit of doing. We initialize a current pointer ``cur`` to the head of the list; and ``ctr`` to ``0``. Then, while ``cur`` is not null, we move ``cur`` to the tail and increment ``ctr``. Finally, we return the ``!ctr``. @@ -266,7 +267,7 @@ Append, Iteratively Let's start by defining a more general version of the ``tail`` function from before. In comparison, the postcondition of ``tail_alt`` -uses a universal quantifier to say, rougly, that whatever list ``tl'`` +uses a universal quantifier to say, roughly, that whatever list ``tl'`` the returns ``y`` points to, it can be traded for a pointer to ``x`` that cons's on to ``tl``. Our previous function ``tail`` can be easily recovered by instantiating ``tl'`` to ``tl``. diff --git a/book/pulse/pulse_loops.rst b/book/pulse/pulse_loops.rst index f7fb73b053f..1bc3302bae8 100755 --- a/book/pulse/pulse_loops.rst +++ b/book/pulse/pulse_loops.rst @@ -102,7 +102,7 @@ This program increments instead of decrement ``x``, but it still satisfies the same invariant as before, since it always loops forever. We do have a fragment of the Pulse logic, notably the logic of -``ghost`` and ``atomic`` computations that is guranteed to always +``ghost`` and ``atomic`` computations that is guaranteed to always terminate. We plan to also support a version of the Pulse logic for general purpose sequential programs (i.e., no concurrency) that is also terminating. @@ -187,7 +187,7 @@ may not terminate. Here is a simple example---we'll see more examples later. Let's start with a standard F* (doubly) recursive definition that -computes the nth fibonacci number. +computes the nth Fibonacci number. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: fstar @@ -195,7 +195,7 @@ computes the nth fibonacci number. :end-before: //fib$ One can also implement it in Pulse, as ``fib_rec`` while using an -out-parameter to hold that values of the last two fibonacci numbers in +out-parameter to hold that values of the last two Fibonacci numbers in the sequence. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst @@ -214,7 +214,7 @@ Some points to note here: * A quirk shown in the comments: We need an additional type annotation to properly type ``(1, 1)`` as a pair of nats. -Of course, one can also define fiboncacci iteratively, with a while +Of course, one can also define fibonacci iteratively, with a while loop, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst diff --git a/book/pulse/pulse_parallel_increment.rst b/book/pulse/pulse_parallel_increment.rst index 45e15aa6d16..087e4aeb6b3 100755 --- a/book/pulse/pulse_parallel_increment.rst +++ b/book/pulse/pulse_parallel_increment.rst @@ -116,7 +116,7 @@ function a meaningful postcondition, aside from ``emp``. A First Take, with Locks ........................ -Owicki and Gries' idea was to augment the program with axuiliary +Owicki and Gries' idea was to augment the program with auxiliary variables, or ghost state, that are purely for specification purposes. Each thread gets its own piece of ghost state, and accounts for how much that thread has contributed to the current value of diff --git a/book/pulse/pulse_spin_lock.rst b/book/pulse/pulse_spin_lock.rst index bf66ce336a9..7b37fab0a37 100755 --- a/book/pulse/pulse_spin_lock.rst +++ b/book/pulse/pulse_spin_lock.rst @@ -21,7 +21,7 @@ lock. Releasing the lock is simpler: we'll just set it to ``0ul`` From a specification perspective, a lock is lot like an invariant: the type ``lock p`` is a kind of token that states that the lock protects some property ``p``. Acquiring the lock provides ``p`` to the caller; -while releasing the lock requires the caller to give up ownerhip of +while releasing the lock requires the caller to give up ownership of ``p``. The runtime mutual exclusion is enforced by the acquire spinning, or looping, until the lock is available. From 1116eedd76149a6b6eb2124f89237cdf7111fa3f Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 14 Jan 2024 07:15:00 -0800 Subject: [PATCH 33/65] one more spinlock exercise --- book/pulse/pulse_spin_lock.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/book/pulse/pulse_spin_lock.rst b/book/pulse/pulse_spin_lock.rst index 7b37fab0a37..87971d02cf1 100755 --- a/book/pulse/pulse_spin_lock.rst +++ b/book/pulse/pulse_spin_lock.rst @@ -165,3 +165,10 @@ Exercise Redesign the lock API to prevent double releases. One way to do this is when acquiring to lock to give out a permission to release it, and for ``release`` to require and consume that permission. + +Exercise +........ + +Add a liveness predicate, with fractional permissions, to allow a lock +to be allocated, then shared among several threads, then gathered, and +eventually free'd. From a736798516203270c92546fea615eb002dddcc60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Sat, 13 Jan 2024 19:16:55 -0800 Subject: [PATCH 34/65] typos --- book/pulse/pulse_atomics_and_invariants.rst | 4 ++-- book/pulse/pulse_ch1.rst | 4 ++-- book/pulse/pulse_conditionals.rst | 2 +- book/pulse/pulse_existentials.rst | 3 ++- book/pulse/pulse_ghost.rst | 6 +++--- book/pulse/pulse_linked_list.rst | 4 ++-- book/pulse/pulse_spin_lock.rst | 4 ++-- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst index 6e65bf2afed..8156df053e2 100755 --- a/book/pulse/pulse_atomics_and_invariants.rst +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -124,7 +124,7 @@ hold full-permission on ``x``. :start-after: //owns$ :end-before: //owns$ -Now, if we can currently proves ``pts_to r x`` then we can turn it +Now, if we can currently prove ``pts_to r x`` then we can turn it into an invariant ``i:inv (owns r)``, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst @@ -134,7 +134,7 @@ into an invariant ``i:inv (owns r)``, as shown below. Importantly, when we turn ``pts_to r x`` into ``inv (owns r)``, **we lose** ownership of ``pts_to r x``. Remember, once we have ``inv (owns -r)``, Pulse's logic aims to that ``owns r`` remains true always. If we +r)``, Pulse's logic aims to prove that ``owns r`` remains true always. If we were allowed to retain ``pts_to r x``, while also creating an ``inv (owns r)``, we can clearly break the invariant, e.g., by freeing ``r``. diff --git a/book/pulse/pulse_ch1.rst b/book/pulse/pulse_ch1.rst index 4b4f3f00878..5f8f8ebac40 100644 --- a/book/pulse/pulse_ch1.rst +++ b/book/pulse/pulse_ch1.rst @@ -129,7 +129,7 @@ being the "separating conjunction", written ``**`` in Pulse. [#]_ permissions associated with both ``p`` and ``q`` separately in a given state. The ``**`` operator satisfies the following laws: - - Commutativity: ``p ** q`` is equivalent to ``q * p`` + - Commutativity: ``p ** q`` is equivalent to ``q ** p`` - Associativity: ``p ** (q ** r)`` is equivalent to ``(p ** q) ** r`` @@ -166,7 +166,7 @@ that ``y`` is unchanged. :start-after: ```pulse //incr_frame :end-before: ``` -In fact, Pulse lets use the frame rule with any ``f:vprop``, and we +In fact, Pulse lets us use the frame rule with any ``f:vprop``, and we get, for free, that ``incr x`` does not disturb ``f``. .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst diff --git a/book/pulse/pulse_conditionals.rst b/book/pulse/pulse_conditionals.rst index 51ec19cb5c1..ff8e638c9e2 100644 --- a/book/pulse/pulse_conditionals.rst +++ b/book/pulse/pulse_conditionals.rst @@ -140,7 +140,7 @@ Rust-like than what F* uses. * Each branch is also enclosed within braces. - * Pulse only supports simple patterns with a single top-level + * Pulse (for now) only supports simple patterns with a single top-level constructor applied to variables, or variable patterns: e.g., you cannot write ``Some (Some x)`` as a pattern. diff --git a/book/pulse/pulse_existentials.rst b/book/pulse/pulse_existentials.rst index 3df7b967f0e..6f3b1fa9dca 100644 --- a/book/pulse/pulse_existentials.rst +++ b/book/pulse/pulse_existentials.rst @@ -38,7 +38,8 @@ never actually used in any other predicate. :start-after: ```pulse //assign$ :end-before: ``` -Another way to write the specification of ``assign`` is shown below. +And indeed, another way to write the specification of ``assign``, without +the logical variable argument, is shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst :language: pulse diff --git a/book/pulse/pulse_ghost.rst b/book/pulse/pulse_ghost.rst index e959b1511b4..27b51f11003 100755 --- a/book/pulse/pulse_ghost.rst +++ b/book/pulse/pulse_ghost.rst @@ -80,7 +80,7 @@ the rules for allowing F* :ref:`ghost computations to be used in total contexts is described here `, and the same rules apply in Pulse. -To use of ``incr_erased`` in non-ghost contexts, we have to erased its +To use of ``incr_erased`` in non-ghost contexts, we have to erase its result. There are a few ways of doing this. Here's a verbose but explicit way, where we define a nested ghost @@ -92,7 +92,7 @@ function to wrap the call to ``incr_erased``. :end-before: //use_incr_erased$ The library also contains ``Pulse.Lib.Pervasives.call_ghost`` that is -a higher-order combinator to erased the result of a ghost call. +a higher-order combinator to erase the result of a ghost call. .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse @@ -301,7 +301,7 @@ similar to ``Pulse.Lib.Reference``, in that it provides: A somewhat contrived example ++++++++++++++++++++++++++++ -Must examples that require ghost state usually involve stating +Most examples that require ghost state usually involve stating interesting invariants between multiple threads, or sometimes in a sequential setting to correlate knowledge among different components. We'll see examples of that in a later chapter. For now, diff --git a/book/pulse/pulse_linked_list.rst b/book/pulse/pulse_linked_list.rst index 7ddb3338060..485f6a7dbfa 100755 --- a/book/pulse/pulse_linked_list.rst +++ b/book/pulse/pulse_linked_list.rst @@ -142,7 +142,7 @@ our proof. Trade Tails +++++++++++ -Our first step is to define ``tail_for_cons``, a lemma that with +Our first step is to define ``tail_for_cons``, a lemma stating that with permission on a node pointer (``pts_to v n``), we can build a trade transforming a permission on the tail into a permission for a cons cell starting at the given node. @@ -200,7 +200,7 @@ can get back the original permission on ``x``. +++++++++++++++ The code below shows our iterative implementation of ``length``. The -basic idea is simple , though the proof takes a bit of doing. We +basic idea is simple, though the proof takes a bit of doing. We initialize a current pointer ``cur`` to the head of the list; and ``ctr`` to ``0``. Then, while ``cur`` is not null, we move ``cur`` to the tail and increment ``ctr``. Finally, we return the ``!ctr``. diff --git a/book/pulse/pulse_spin_lock.rst b/book/pulse/pulse_spin_lock.rst index 87971d02cf1..f044eacb656 100755 --- a/book/pulse/pulse_spin_lock.rst +++ b/book/pulse/pulse_spin_lock.rst @@ -53,7 +53,7 @@ allocated ``l:lock p`` Some notes on the implementation: -* We heap allocate a reference using ``Box.alloc``, since the clearly, +* We heap allocate a reference using ``Box.alloc``, since clearly, the lock has to live beyond the scope of this function's activation. * We use ``new_invariant`` to create an ``inv (lock_inv _)``, and @@ -98,7 +98,7 @@ The specification of ``cas r u v`` says that we can try to atomically update ``r`` from ``u`` to ``v``, and if the operation succeeds, we learn that the initial value (``i``) of ``r`` was equal to ``u``. -Using ``cas``, we can implemented ``acquire`` using a tail-recursive +Using ``cas``, we can implement ``acquire`` using a tail-recursive function: .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst From 9aa592087e4ef4771ebd312573d9e380fefa1961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Sun, 14 Jan 2024 16:21:24 +0000 Subject: [PATCH 35/65] pulse: getting started --- book/pulse/pulse.rst | 1 + book/pulse/pulse_getting_started.rst | 72 ++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 book/pulse/pulse_getting_started.rst diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index cf155627b7b..4c49f4ad974 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -76,6 +76,7 @@ dependent types, refinement types, etc. will be of use again. :maxdepth: 1 :caption: Contents: + pulse_getting_started pulse_ch1 pulse_ch2 pulse_existentials diff --git a/book/pulse/pulse_getting_started.rst b/book/pulse/pulse_getting_started.rst new file mode 100644 index 00000000000..8cea4004718 --- /dev/null +++ b/book/pulse/pulse_getting_started.rst @@ -0,0 +1,72 @@ +.. _Pulse_Getting_Started: + +Getting up and running with Codespaces +====================================== + +There are three main ways of running Pulse, roughly sorted in increasing +order of difficulty. + +The easiest way of using Pulse is with Github Codespaces. With a single +click, you can get a full-fledged IDE (VS Code) running in your browser +already configured with F* and Pulse. + +You can also run Pulse inside a container locally, for a similar 1-click setup +that is independent of Github. + +Finally, you can also extract a Pulse release tarball and run +the binaries directly in your system. + +(Building from source is not well-documented yet.) + +Creating a Github Codespace +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To do so, go to the `Steel +repository`_ and click on the +'<>Code' button, then select 'Create codespace on main'. This will use +the Dev Container definition in the `.devcontainer` directory to set up +container where F* and Pulse can run in a reproducible manner. + +![Creating a Codespace](img/create.png) + +.. note: + + This will consume minutes out of your free Codespaces budget, + which is 120 hours a month for free users. If you would like to + avoid this, or do not have a Github account, see the next section. + +You should be greeted, after a minute or two, by a VS Code instance +running in your browser displaying this same README. + +![Loading screen](img/starting.png) + +![Opened Codespace](img/vscode.png) + +All the usual F* navigation commands should work on Pulse files. + +If you prefer a local UI instead of a browser tab, you can "open" +the Codespace from your local VS Code installation like so: + +![Local open](img/local-open.png) + +F* and Pulse are still running on Github's servers, so the usage is +still computed, but you may find the UI more comfortable. + +Running the Dev Container locally +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Dev Container configuration contains all that is needed to run +Pulse in an isolated, reproducible manner. If you would like to avoid +Codespaces and just run locally, VS Code can set up the Dev Container +locally for you very easily. + +Simply open the repository in VS Code. You should see a popup claiming +that the project has a Dev Container. Choose 'Reopen in Dev Container' +to trigger a build of the container. VS Code will spawn a new window to +download the base Docker image, set up the extension in it, and open the +repository again. + +This new window should now work as usual. + +Using a Pulse release +^^^^^^^^^^^^^^^^^^^^^ From db60a1668af77552dbddb500fac0f3eb6c4c4025 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 14 Jan 2024 10:46:13 -0800 Subject: [PATCH 36/65] space --- book/pulse/pulse_getting_started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/pulse/pulse_getting_started.rst b/book/pulse/pulse_getting_started.rst index 8cea4004718..d93ffd7ff49 100644 --- a/book/pulse/pulse_getting_started.rst +++ b/book/pulse/pulse_getting_started.rst @@ -22,7 +22,7 @@ Creating a Github Codespace ^^^^^^^^^^^^^^^^^^^^^^^^^^^ To do so, go to the `Steel -repository`_ and click on the +repository `_ and click on the '<>Code' button, then select 'Create codespace on main'. This will use the Dev Container definition in the `.devcontainer` directory to set up container where F* and Pulse can run in a reproducible manner. From a7abdf963b1a8964e6e01cc18783bda84fbd7524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Sun, 14 Jan 2024 17:23:22 +0000 Subject: [PATCH 37/65] images --- book/pulse/img/create.png | Bin 0 -> 150579 bytes book/pulse/img/local-open.png | Bin 0 -> 226190 bytes book/pulse/img/starting.png | Bin 0 -> 57602 bytes book/pulse/img/vscode.png | Bin 0 -> 329053 bytes book/pulse/pulse_getting_started.rst | 10 ++++++---- 5 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 book/pulse/img/create.png create mode 100644 book/pulse/img/local-open.png create mode 100644 book/pulse/img/starting.png create mode 100644 book/pulse/img/vscode.png diff --git a/book/pulse/img/create.png b/book/pulse/img/create.png new file mode 100644 index 0000000000000000000000000000000000000000..ec6557de07a09ecb415d33f3d45509eda04c1a1b GIT binary patch literal 150579 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%0DxZ5g9@KR7oS)u5% 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 I)pHK}A0)hlM*si- literal 0 HcmV?d00001 diff --git a/book/pulse/img/local-open.png b/book/pulse/img/local-open.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7d2b3b6d63a7525b3b63b34ae98838c5296310 GIT binary patch literal 226190 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

YUhOL$ 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�L!a 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 literal 0 HcmV?d00001 diff --git a/book/pulse/img/starting.png b/book/pulse/img/starting.png new file mode 100644 index 0000000000000000000000000000000000000000..d41c262a0a2c76e52d080761f2e9be59f0502d50 GIT binary patch literal 57602 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

h*>V*FAbSqtyh8r zK5yA7qtuY1e*kUFa;BK^*wyoeTYddLzbP&@c7nGoSd)84zU!~aQNlI>EOQSKbnq|K z+eQE})?UoY?WTmsWWJo5mA-li)+c_Tg&oxn){V@o%@)LAACMB*X%`-A5EI#-KFiCDazt> zAvUZ_6bUr@P(}A!4p_xB{w%W)agsGeEnSd~QE{z;;lK^sDfnssb&)m?`Wx_CyJoG{ zq2L@>cU=H|>hCWuJ;wGo`jlkYEC*NFtnFJr%Dw-(j6xs0G|L}q&Gsv;VJ-nLwY0Pf z&CFvYsuWW+D$VzSaDJVoW#Q7$%(iUQ2qXl+qMRHJ zRQHgN|B^Q~bXHwJZ>WlZw48+wuNr87W*B8f7eNOG0-7mXAUU`5<}L+-djbxdLN}(U z3YjfR7J^Rt&t0<>F4M2V6T|0-g_CzUtCbb7+>SVi%8`lJ7W`j$)lR~xzbxt~>*)~| zWpQwU@FP;jo&&EgX{v+Um> zFs}OfRhQsdZ+6FhA)4Gx1&bu}h#Pe;`SzT?jUI9I?)e>a&e>EReQ<+2){v4vl1J<2 z2o_GUoW_Mumy``a-kYOo*?(P9TwFC;ma1IEZUIBy>ow>(=VNmZfi2am!6C%)?ACgi zy$mC9z<6>c-uXIslXwtuJKQ*WxzFSW_w0N^3zhLinfCVf=rrTvGNOnat!-@swCfJY zb%LzVUVS`kKU6K0xQf`ZdPx-bJqlr2vuKcm!|e)$6(ibO?&CXu*p752`R^?&H8BTU z1BLx=hbZ{T?@^_9Wc2gQrlQ#5uq~AHJ(EX2hU$EXn<%`Ycdowg6CyTrm?7t7p$rWO z6iJotQ1UlVc)8#TqG;cIBilLo(#vZR{7knF+aweTWBWwD)| zJw@*32Tlx{gnf?RPrEf9lGodIFO20I*^tuA?I}>uX(016A01@O-PQ@V4zfy`A0R-) z7t!h%Utl(aAMJH-iwO>vifIW#t_MkZH9U|mzQmPrOo3ddFl-_gg1^i*=diY$70~o; zmpgxQw=P1Z`s>2NKLhqJ;6vXza`k#-R1~t+Y!RYXlLX?t=2nR^F#q$v5=@6Z9um=) zanrI#H)40!^b)63#l9q*#}8n)ArF84MuSA%?1?O1zPw)aQl)P3lH(Z^OF`VzTcI|3L9ool&v84wcme?V~gGH^CR*jP zr~DjzHn@0T z1lynx^926-MF~~PIU{rDq2eA}W&++@d$jK8!3?_`G1{_LdKPiSW7!2S%)A@WK#-?o z1r(U`ilgZ5(*>JN+aVG}X)K%1npEt=6I#So*p{w3;_bB$66E(CP=)@xP3r-P?kV7o zbEV>_H5=@Rf%fLUGPoER-+&;?c&1d#VyUs}ahNxbZ-#b^cN65qUj0h$Gxd)Tdc$R^ zCfZdJ>P1RFQ*H1IW4oQdGm5esQxsy(TQkB?u>rv3&%e4j@H z9h*~tih6liM1)CWKAx;7hoz-u@egFctz~Y?H(u;y`LA4!u5Jw_O+V081X}`Kz65G1 zyI96XknQZ+-||o$f2JmLS|*b98C5}w>*i;1c~E6oCO{0h3iL77xVPHy#Vbc`QgiHM z8G1JF>ou(>A2U_6dXW`S2*Sc@z9HD=)9%e04tqLNW*%D;f#FmH5rv0s8UC!1T2*Xq zVvZ8=;Rh(Rpe0+D+|8DsFyjK$H zx!*K)8@@*r9VqMDDs5=4(^}H0?5`c32d2vIt-)k2m*LhIb`yAG%^?;+^64Ur6|ox; z5e_>f{a8oCIDeouqs7K9Tv%CYlAjiqEK4q^!m(*)C&5rltAMMgx^epA_N*-;c{aS` zVP%C!5`9SYg4*^D>2hjiXyizW585R*tc8m_i!$z@Z_Dd#wT)N(xG@VJNm@% zQ)lpy^Ly%c(!i5O5kaya(LtO3(U0yUi#E;idB%{gSmTtdKnNW-1dHlrXI$MZ;cZji z>3LsTRW{byhJG{**!{Pz?R8Q}w zgh1Z5cTkIZQrkaj5elW_0vDN`Kn=x8_CJ2cn|-oV`3YSYH=cNV)KtypS0D~8$F=wC zNVp=j<00N+CDOX!iGH5cg&3fXf#+1<1W<4PsZt={-~yz9T|m2KN3iep-dN)~UW_$| z%^aHR{?ym9GUQTQ+pmYRMBI4r^WWkTCPOD`7h4shij{NiwoHtZ9877RMdYTDq^7W=3U>|c!uJDqU`eHH1j;m+tdppA3+3R7cb04HwW>EV5v|j zI}yG4-Rf3MR|J=mUav!9&0{Z2uf&L#c#-g%9 zzGs z>Jh;U?+Q3PD(3($|SqD(> z&y-HJX*P4=RUzMRTypYd>z%6#{P0@{aPY)vNx*%4Y*_Rtcy?y3G{K{~;bq1v^6aBA z0AaoQHZvW9Yh+JGGpvHj7z8aJinVA4!D1lSlfCoVI1dkeqt94vTy@T$_vaE zV^Tg&uV}APwNFRfqw))2?1wbV)pr=)?cu^dZa$o)F^*H%!vDD-iO)4e@ng&l(A?|$9{Y0sZn76KtTEMr zfD;+N(epgt3Zoe4sA4fysC4WNwk+LJi*Ip<%s`$&V^nGXxNa9QA#z3D3q;-Hi4!O=AEHM3{BC<%WBys;k zM#)@_2etYV(TgX0ib^&Y#+h=h2+~K~ufH4kS0hz>O49;RIW>A{%ip|tGNU9NY^l||Ba*}47i`cUL#_Hlg4 zg$Qxdq*}VK`Tdl(+NHj^6&%^iZ*PX~y*O_jZ5@j02+BJ$1tn@HVK03tAeXausGp8$ zPkRhyeQh&2Y%^0Mzt}3>ftzs%>%%(kYv6a+-t-(Nhom50W?N1zeyyhR1iP8bMQ>FcBPTO<$@k@_R?+o;(Om`NB<%!R$>iAU%U z>Gd(Lwkbzm4hwayS)E=%FADj_?3|x+t26w;c||)6G!*SX%!f$m2I)aH7}|+2GilJ| zfL$~iGCpk-xMO4rF&SNqsN@_#&oICmTF3F#!h*Ef&TF1#3|)4SGmPNU(cYew&_a1Y z?ZT*$zMN=+htY#+0${uDde83O^PaOxKjd#QmK)nrLZD$z7WFUWa75g$`WstD zmkc+)UD49U1&tBjrh_Njmv|+a^zBJ%ouL!`j1xSwa9`Dp7(7jjxIX288yH}sHfb*l4g}EgusvcogVjx`VZV|YivUF!nWNp`)60X)sn_l z{M3C3>~1RBb;CkNK6L+`)vDN_jnuQ`P5$epT#*l~rY2@o&4;pw%jn-~31`&|sVV5; zf`?06t=PTHZFuPP_}tj|F=y1>BpYvV<;9Ow`vofU^0qQ#0I)d*;S8iWmZ9D$A^(X3 z{TV}3?KG{FaQm^e%ICQL#_e$K>(OFElBc;(*5EtNLjT(>+{1;slHnEr8ruveHU(`1 zgfR`sC)LEm!^831PpE1~%INKe=-(c(CiCv!@~ME}SEopmjQ8#|Sw^{J@zh%k0`efRB=7k7H3!TG4j73XrXKGU$dvxhx$iJ zH$H6BL3s-Mt!0>v9L#H(q4#eJ3aAa!@9uM|%ncsR^XwjxBHSp85JVrJQcxks^2YFk zChqPU9`_0$#HL@tCVK^B>kyll74C_KPNZ?Cwi)A>b+-$(4{UhgCr*2-p3KmRZwk<0 z6Pu7%U<2nV+wLDq<#(0jTvtLLE;;)Q$4H#nZ6dpScaA@FROEz0 ze7kwXwA1Zz3`MeX42r}Sko!yhJ-Tz-OxhtY-OBOq?=0%%WtJZh$q@(KKRwOAnnWDs zm0#x+_Gj{o5Hp2;Mc3;@_r`i=z+R(utU!jD1I+iUB4)><@9rtZ>L2cDCc5?b{04|eMfnjcT~vVGNk zzt`)^^;z8B+kVCeDm4IHS}Y8!w;9w?O^<{;6Mn@!DJy1YZ z67tc=b_A@knSj)W8%k?I~Fm%WYswAf8wr$?m>cZu&2QvxpYpJ{0 zS<$WlLHObl4pUOS&QaxoT41X=pE3g>HExB5*JC5px=zG}M$W zSgbzw)z1cRnJnV_W_>Tv#6%;}nplfrjc>+nQM-&D#e=p6VQytenDhwEV@65N2gY*) zVPA?-FW*-K+4AeSTjV4 z9!DMCDHSthSsnTU9dHsc>`y-Z9hX7H-WMvAS4)2mRZbHPRLnY1oC2+RZQCQ;v)Re= z!^A<C%2vO<+A^f@) zzd@?|E6g^r{?-NN%w31AbRh7T&aE5dL>E8%tswJE_G?ewiMq5aodo~?9bsL`aItD@ zTQFbF+FNfp0V*Yr+>(9A>THJK>P?u%ua)op%eG%_bg=6O2GF57L!jZ#t=f{%D8b;} ztoH7$nu6FLslg>0%BqbL?N>t|I(`*j?S#(O4x&S}n!l~8rMd#etF=Sj*pRa4jLV8+ zo>Dc9!&bFpX)d0yR)PN#3#e6TY-gPM(up1|)SUu}SPE3PVR7I$AnGtOeGmHBY9pxL zPO798$D0OlGR2?Ev?`|0(w-}A-fA9wKMrOqO5DR&+P=zj;ZSpPOUFz+6Ov`0k z5Mb~ocdRchd9jp3Poj*CkAe1a5%-)!oMIrG->Kw{80kd06=b351XfKq<8rb_N~*Wy zg(5aFUu4@^y|0GLQFbtvNH9Vz-OE^Wgs|qNrx!f9PAhd>NK!1JO9bDtd(WBeXT&0j z_^~qmI`49^d(Z+_Ed@@r&}s9-a37xdM|`*_*kFYt)Zd*|vjsny-~?Nj+#tlos}APF zK1E7gz|T*V%FpGIf!@4^LNl-6* zsy)a8c5|()vYiV;5Kt3qE$JV<)()vyfX09^Zqsyy54Bi+Y#QVbs#Vky0~h=7AGEM{ zdvF3R%V4)zgSmK6?^>?r!PIp-?)P;{}di}bSjTQVgytzRJ@`u2e%L@|H4F=aHu4K4Go-`-p5Tk%~dA- z>p)fXF4>*KlSq$bGJgL1Xa;YIGp1Hb3TSXpRZdhdG^+E1PP_2b3L_WsHSZ`*M(?c* zeB<}@&_G1Th*hBqfN|^HRmzX6+xf;h zYtN0h2}+l?FC{Ch$_rCXo|~RoIw-ld1{Ck;-c!(1v)e7<0TA^wvv}Zl_&h-3+ikj>x>WtEAzFlYy*ynxTUCYjtKIWsHIn$kQS6Xr5X@ROq zKAxEn!rQ0urP^5@gK3v=&tQ|zpJ`e8%KjQccYqZbpt6?-*=bb$%=anvc%p!h~JyZb{+8;UDPL+`t zz%z;rGM~Fq8>Gi(+WU9MBvMmQyf}?xi!Huk7I_||pqQ))_9K=ftDx7o(&k4ygzW4V zX*RRt1RJYT(BdqT?9P8;nt*=`wkLQWE;jdM8k!3Si^TA`w|FtbUK#)n%Sdm#YLaLq z+66G3ap1^2memjCB!iZhN8ei=8QbSxt0tE)7ix8(U&^QnkKQa!zmf~r{hcwP3^SkO zScE68t88ykX9Dn~#_bhV7H-?l+~}XJV5l@B9UTIs^&G_y2vZ*b)V09{NKE^HR^L`) zAPJJR`2G)Do}tIfEhUkmd^1dAN=uD?#Iuvgjed;Cjv;=1YfpY*Z3lv)J=a60=M7mV zPkmz!>je;We-Biq0?G|#Cj=(F*3c3S6(clWjG6j+$uW?`YH9_#H#JJiY)z~u45+!N z3CCzH6GnH@8a+Q5v%l`Qh-KKGgHBD3O5EaoOGf&LpCC+&&yY-55_qxF z9=$KdAh3+)aQt$9Ao+4$+h!5xCyI;Ry&L00$bno^igS`lSV5Eu;km#6#7|7e+iofuAW zc(B)IHe6)XTXrg|*7G3$Y`2t@m6a<^`cc%x8(Uj|!nYdnP~JWnH8m{2k;~jqB1;WI z#9jH*2EAHrbXfbgpVa>f;?hUQQO$k-@-?hby&ENDXybUcLQe%3U%x#yyhnx>u$=fw zOzEP3@**0ON+6L-;}}`a`k~=pYc(y+4#y7gu5WH`vcu-8N;ToVY8Wyme(5g2u=JzI zqk0>4eXE>68v2EL1Gj^xJys}Z9MpW`nC=U>M97yp^Myi`CtLACdAbq|VDVc8j6mtB0d;B}h@y+3!4>s`qE(mZJZ_?+jY3%=&~RHYiM*MCZr)`-qCKPMC~IvlmlLz@ddGofnrPz5{6MC)lbFaM}j|Jp>>Vy2xAu zbe1ll!xS-MDeW5V#;MD#`!oAV0~dLa?)2>D9w4Wxv8UBesp_^liR+oZ7qy`s7-%HF z+k&c|YBu93=<7D#fS>x(uUAA%)UUH0bEykW^hnETnsY|0U_tUjLK5+eqamLu==tH? z2Q5*lt^XRjSQgXC9C5^88wSJ1V7ea+5p)&OJf3I2iq$C=P24_OYKx4zI~6$3RT>q0 zjR0)O)6;&^ZZMei34mfl{GfM5t8%xYJrEWgd9SAj#4BB#24qNxvyQEz!0aWphFxrU zvP!;OV!X1R`Gr643&cKG9Czg^*C%}_G=0q{iv!u@skCykQ@k;}g!5wMn;&nqGA``# zDiW%v_bxLEO)*)Jq_|5Y#|Zy1eaYuEOf+W^UaiHtmRo7&Edl5?W0x#`vLzrwC-9+E zs*vv2>6-80HE;hemdXV1uHH=ZBeft%K4qVb=TI&1antpRk!sY&f+vb6QT%#3#1hX zdbq$3UU;!M(MPyE;CPWPQkHcU#F(qVkaTL8jN;#w3RT>>LG36Dn6{SEVjGR~3GJmw zdA*tBx!1*uB2el?BW_3o#slKwI2;I#)|RI)~P*n zIsm({wbBIeo(4(9+z~uW_4Hpn019p;`^#tFMCN7Nk*M)sOSAe=^5U1q_DgP6DSPs{ z09;~YFc|+xZ((g(!=R5}?zlJi6zXu@>;2uA8&0c;Te2fHDD9Jt!*&bNCTIiato9ST z;AQ7Ob`Dh>SsD;@?gZPIIx7_@WPJun^gAG{Q8q0>Z#H5=BO_ILBLUc%vA^qb>< zT_!hszqmt*&ocZlQ;NiisW=X;hdtQ*Xg^2ld(UgxJ*eo_oZ{Y26utWWj8BVxyVJE= zSqjmkgMH*d>PE-IP+K+Limq_nZrPMkjQla9Rh|&%#2A12q3S4d!X&ulcSLD6n{&Qq zdnB7}l}j%eZUA!&{_UNU+1ksDA)P9_fb?C{BjW=6F930wY<+- zSDUg}Tl-o3=T|vu-yr5f<>H@leA`7(rs^*P6;Zo1XTc-3!OuBY$14Hf1rgKqaW+Re z*XF7xmb_#^kqkcVO~l46hcosvrJS5T2A_>GAHp?vaDE=-LZ|ITy7D1ab>G36ftS{3 zx}b+YbznBS;rNqj0zf5)u0GsTo^dVUvAm*!!TD%K1yKr$73u25p8E|+u;O<(>w_(m zGWRg#IUm3l<`{u)B^j&(H4Db;V}qwV-1PK8Khn&?YP>OF&_;H3>KH&FRIAYf*2$?Q z*itKGmZtV4spUM&yaI3}DWLt`%9AxwX(YO7IuwEa)jLBwck;H~;)fF|>txw?J5cZ% z-FH7K#9(@E$%y*SqE#DHf}Z=Z)2;Jj`hO&-;up{Kl1#G25HryBj6+nbbRgh86#$x^ zkcUSjT`Uj4@2R!5E=Q6za|+xj%1h>EdmEd-gO6PG)^iM1#_XKLzEn z8!#0C%J*mIMajcbRK{g&@FE9_fUI z*_Vmc;x!y3JMLCs2%C{XYHaoDu-5V+X;@)-Y&(Vu>hK*qKe#&JVg&~>Gd*?_^Fxc7 z`f2hz{iRXaEBb(PM{Ix(%cs#rsoNbTQDuf4e}q2ej+D3Ra-9~sA<=yfp2~N#d3b{Q zKw<-v=`KBdUg29Pf6N<$7qUit0f(i5M4Aut!dPRh2LQ+=SyKv-c6zTlJgG%YsZe}1 z{TP{7wHV)F-OkI(>M>>-7*4S2MhoABr)UCNg9i7<;@u!OMw>@5`{%y^B}U~~l13Qc~=@05*^+;16@_gfY{fnJFGMRQT|jS)j?p&!3hv^6}=E}w*aozPk0pIymSZ&TUP>HS9HNGpGOeKTNSjv%>dViI{7*OI6pH0JsxDUpP>W6Sss|XQ1ZZeQA?ktGH9 z1FciI8xs?PE@bIVm$1AEG`j@$?7I*o{BNH~JjE_G7_Ut(c;hBEXT?u0eJ~U{m@z0m zp#~M)D-O?N1xy9&v0Rz7g~6%R^E2E1aKq4tdAkHKPij4moiiQ$?NG=e#3Asnb*wgU zH14Rxm@>m-eV0!+-`#SHU3X1-R41V(9?JwxV#cvR%_cFUMo+G;L$14{CHb);Qz(|! zwz)q-JeQYFFXp)4KC(USonW-I&@^97$ZNyi^Owm{oZbOc6p}=3zH1)pp~|r>i#Kvt z!_WtXrxDC{!gAx^gt?6B?{*hPMCD|(PE9w#bsa{#rdmznvnzz>PwY5)8Jic0-~>Xx ztjQIV!UA4C1B383Df!*g1&m=*Dubd!7hlV29LZ3`KGh9A@`8$j58vY849}7?7ecql zrZwk9S{nc^Y51;>hDHXgneHdX&e@1sh-`4I$Yv{JJN@L?0n@fd*1&n?S^uk_QNbk1 zSDha(>?_6=$5I$D12O~eL%H8_ldYIk3MR_#{S-mZCxVj?d5XyBJ)t~I=#1*%FXPu> z%JRSN9dv%pe2_JkT}h=16PQ2b$%?ZU(R;oebpXrV@qbbGmO*iCVZUbx8eD=q1W0gg zoCFURv~hQ6+}+(1f)fakAdS1bTd?5n?(VZW=e)OW-T63GQ!{+%4^;P-wb$C~vETm* zA6ayLm+RAlTcB}=^9!4$RRUO7yaqRg@!%as=Vc|1{mlMkfx5WXFPSxDh8ds|?lU

#-XaiR>pFgJ@ZmFTCh8~?zTHJZ>uE%4AKFu9Byv&K#Bwl&)1F3rR-*A#mN_!QTi7w_x*@CUFS zyPN2JalTgJ%k*x`DIdBT(lB491#9Siy5eEVw9r@4J#59WFjNshxkwaJs9ML&FsnT%JW#62 zL-;LlXkT+PZO?5_6y7Qd$NwJ-Itg|5rPQ{bl6Y48F969Xu8V+x^&}o|P($u>0|SJ! zPEJnpvsa0ty@_q%yx7Q%OeQ|y8)7;#;)QIL?3^BO!oeZx2MB1Gg&1X8sMlB#HIcdg zQ_doY7bsIlu4{|9vVXf|$1HXJZhTSjWC-#&mJ`TvfW5hmxd{KWd;O8E-4|A*8`6O1 zH5?+y>ONbGYV1Xz4-an&%*Ra_AJlyOE$Dw5319cWIC}zoJTQB$mK&!F@apy$Q>=c3Nxrhe;NjnV zeAMZ^p*|V3I9ZXJ5aAW1Y^fZzM)GofeHXdeRb9W^Sc z$d}~xNS%B5P|B_U^sdw^lUtZ^f3AuY5yM+WoN>8xySN*{_t4XdvPZ>txx6rcIScWw z6@=pd{3HFC_xd9*VduRQ?!=#;LTRAGh7O~ko3trBZ92e!Q!Fuzu@i|+%1|5@O!&Tz z)9fJnSrC9G*dQ5i?e2l?rtfDzJ6mkzaTe$P@VsJ_bBVYEnPxp+Wfxy-rFu#H#+|yC zOE0=^GOcgCF5?<;-1+xs{F|#v3rivUnz8KyoLzG&pT?e+M!e;cl;EcdC2gtv<*sUV zs%`6a&II4IN;kQ3l9C@sidU^zBK=!0?kM0aeAp;=b!S}a>7lKS?~LlS21E>4XKjrI zwDk&0aLL@t@%F9hymj_Wi1^nUtLpg#K3vDQli^?h z4*H^`ge?(8kcIJb?Cb$rSlUnwatT92suv-WIqZ@ThxY>~*I%h6G4b*9KvO_8&1b=N z+1paIh3~~bs6BI3zO*}C6Nf`USOYvvw}bNDlLg0-0@5BR4axaJE){_eo4d}drup5# z@chp}tv#EQ{kssBunx|NxHk!}riCZRZDrWbHWR`uHlzKH>9mT5z?HuZ_lxM*9646l zyot9k3JK>IaKrakp}RMy>w7g2wGvVQuX+d2k;y{dd^NVqB^VzvYL|V8*L$KTjuRFd zk6R*3Tc7S~^zz;THJ>5eOKz}_cbA=^IB->W!^v;S2j&`{h2j9f7w<#V#+d7Nd`8*@k9 z?MdoR@&uf#14mxK|3$||^55~)4pb_9L}35&Z^JJ&2Xp+zA6AK^LM)B7 zO5{>G3tU!-pguqXG~`9tBp70hV;zWq1`Svx0-oZx$XW7m0{i`$TNu_Wi1427fvUB) zbI&6N`-?0V5Jj6!1zAVDXrJs%6{kTRm@83(ozLvuA7+-9KkC&<(Er)*>gxJwHj+V~ zu8c)l%&&>AFt~&qD_1TqdTq5(^?KYGK8=K7q3`V@w^$N=!ys=R{T)sh`hK}%{ z`z1|<26PRr){9~?iPuq47?)=#H(aKf%p_|?q`b#gZ_kY|04jB}`6mD(kQFk;2NWwnp$kg!_}+f{Gk&HWKz8B=;MBgRq!eJ( zCp0M>jY*P&@qx1L@%@?k`Od{fQgrm+wnj;~R%h{{mmCxR&5;gqCpksxEC|LLlrrc^ z#WLYiI;APNa~N=Z9;&U#AXT8_hN2nsREbU@!auaI;;y*3c!6n&v3{r0trFC$(dF<( z9?!4wr~yStNa(lFCq*_lhcsYBVThFl`n8m_5y4#L`=*Awzk69d`d&HN*yLg0vZH;z z;Ub9KVi9p8S())>fBo@1r1HsbxDr277ejO+i+W|U6E}u7egNP;R-bP%$x9+7pvfYg z`0$2vIH*(!xFw#2Aa@6;47h|z(Zz*@i!3ofxbKBzh0dL}K0nI8(2tnw)ziA20HDio z;FjFPV+$)9xqJYxvcej(an{SkNYp;-_JenYU?u#xJ*Rh z8Q+242Dz57#0?#gf!9MsGa`-hDzJTz2{SY5)vZ)%QZ&F^ibXXBIxry8D`;SOTpyw7 z>FG@#c#D7QbphEfMNJl}6(Tcj76TI~naAERI0O=9IpC^E%BHVeM{HQTzrERZoaPW` z0{a)r(8*<}8C@kJSZ=GTLqAPW;5Q5`n$&Z^SulLKIla?=c`sAZ7Xxq15&_6+Wa)tU$h@=w9A=nJxw&q+{L zelTaUQv3Yc_u(7tc1fG4?{o*(pj5f%h4}DA!c`H6BlNaLsB= za%dZM{foLP5>`}Kck8$={+{#uA}jkh^q&QT1r}Nt0toW2_ssuX^kzRn#n*NscZhY) zM+G^y{|N4$RBl^z2n4cFXa6$f|BDij|Ni~g)yoC^hoD1|*o^@+{H&(I=zs9?+1c4u zV0QpN;xlW1^6+TbdkOCWdp0V&x+AbjPO@BsH-u3Bxtj#k|7P#zPk`+=pDIe(i;axz ze4&^ByIt`HmqQ`|y~(-T1TI?bpL@>!_CM!Je@PjTw*BevS46BD`N1DLG$i||_|NIG z&*BpjeqRYcJ)Fx|%Try;Z3rR%drtKKIp=4xHk*lDw4&l-``a@Uz)L0Hqu^$k`$s)P z(LHWhxZgVl2uBvYXcz#*KS9YqXKxTx&C1H+Wp7?8)%)oEuhd2Y7a)ny)g|^qw2}V@ z{I?u6>@x#CDv67W%N^#+HPOoe7qD03d405(c%;qma-jb&JCPkhfni>2vq%I8T`Q`^ zf`FXz)z#IWrP9AQGEZ~Ov>_C-FdcVK;;<}w6!?#@BQV=Q{|O-cGgtoo$PWVE^jrTO z_n)8t_mNHYADjQrGym_y80NpB_}~AD_y314ss{sW56NxgtK@$|7ysKG821C4&|mB8 zqs2+4#d@HWu>D1w=>Paj+)ypsoi5E33WHVvEROU3#>Ow3f3fZedo8C?e#N~+VD-}@ zJ+{d$o~gf49PT#CeL04T1~8(3nHXjkZvXGYAC`|)q^~@DVu|`P>`=xY>oFiWn6LFx z`$a)XDg1vN>Ho!NrM|tf@yX@?7z8n~@0mlBWdZ-+*M|9jreC897_0+d1p&*}@)g(` z=E6pxg?68Tg4 z&O4hPkXs=|IoZn3B49OtFVS57%D@cu&gkV~g7FzSWc|=dSxZ77B*X3s|{6!h5&pIw<~Qia^Ml zxk;V$mlAl`D7TTV_Sh;YTqr3?jZ)tUVr>UMf4Gca*(n|ydc(-!X#pEf|8-TCAkpgr zaOlNk57uAv6SJN4NCkPfz%8#6-ZYQl7avyH7w|>&-np8t+;e=|H`_Km8XeHM1`})0 zVVxZzBE>K{K=x}m$$5xTBpPn!I7Gco$wV2?e66{7AVDRsVhCvZt zBwti;6alMgaCbBdmR2_TCq^#xKdlNV;|r>(2=4NeBjl}xuUuC5Tl^3`>bm7HbTTRe zqOn}cRn!|&7lQS!&QmP`FzFwdVeuMwk7q!CdRm`4Kgu*|;HRquWm)vpn-l>;cW|Dm zF&+HDLFOtqTb-qG2T!!QF$Vz>>|UaivH}4;vBE0_13K4W6n5?NKm6+0U^eGJ+PdhN z%aW65vj=(tZ#h7?Mw)%O7)kJL^BS%4D)6lO%8vPd!*P_ND)4F5kZGvDO7(xcZNhK% z*PxeD3%jEQ-bdYAOF+|(!4n$`EQ&hw{cDlq4Er`3;|#UD)dt3FdL(kEBF8rWx9d`o zPjxpxN|uR=t=F(hGv-iyBF&6G$yldL+q1#>t0JO$ZWF$}RvGUn-!go}8k?{u zFcr`lGNU={g+2$ZYE!c?&ah(XT(ql{6O?@*fd#4TRw&uo9eR0Tj}heL%8M;~M$*eu zr^RXpV6;d}jBw|6%oo%D0u|fhXTYIeBH_y7a-Q}WEk0Y3vOhsS-({|NhC~K2Qb13A86>2&S7J-0_8shuzRqYfn^b++y{wu zk}C^nf%{-yzpJOpRTghi+ua!E4h}Vo;6PX0)CZ*4;$VpP0q6N&vS;&1~ znB?(BZb;n`^8@bK0XbGennipyyTF z^0FSe7?|*1nJh(n(x@r2>vBV;u6t>kKghAhKOk>(bfv)~25N`67=l5%&*QN)*hu!Cuu>qn%=WSIHO z18jIas%2To;25x(O(79Fm;yLdNve7~?KOA7LS37g_^YNLmyKe$LAby(2exkMSP>jJKNjK#c#sUy_((4;Z zH+I8s9?`+MRXUERXIlO=sMAQ%Br89!@nraI;TSLdBcpfZdBbR)uW19+`$GWX5m#q! zye3|9Nq*UGy5l78_%G25O>C~GDgtg+LSEa)5~)Jz!2N&YJa}??);79BmVAe=VNto8t(E#)?KG5;=6F=TxPa}fiKdx6!PpyRcl%6?v?TqO4uHQ8e zbp2~-sA$kv21Fkubq)j;sZtkCeRqc^cE^6`-t6<$tS5iCZbC6qUbWYY^1qG{`64iF z5w&H#$hvcjf6{?-Xh0{gqDq(R+d9aAgzWXk`Y$Yan7L`%k+x?4I_mBbg0Xa%v;3ii zd|7nF7tiar0J^KQSYDpM>G-+mXnDEN37-AUcrP)Kel$1#$c$8|%WMgyHCpd#S9kk# z)o64#bxWX9#Id=RC&SF^3$pSD2`;Seu7)rDND~FUHeX7NW;NLhh}T~>JHW<64xqXRU7F+S34kZt+8x>Cz#OoE?ccpBysLL>yWnzbBFPu zf-_&+_Ht%hV;nN4DZ|VTDw}`ujcJ@}zeH2aFybPXsCPgVm7SiXjjuP>D;t{6;XQ?#FsgVV$VNVmtiuS5E zTvNzj^NLRf$vs>LkniO!Yg@y#W?Mf>MA02R#2{VAW3wd*?Oe|6VSXbrpdpHd2}@QRLvPnMT~2AR6&v#qT^yc-+Q3n|ja4 z`%ZO`tnzenNY``_JM;bzMvjmm(gHtfX3>c_;v*Ep@i-yF1^q*Pul?bu!2z}xJNvVV zThBHwe>*eiz}5E&eapucyPl9JRKlEA$ZD?p?XUhobtO`KH@W9mIG(Aq6Rf(Y#_N6( za-{m{oV#jW+g!}3!8*LOu%IGXLM?5%*Fy{>f{#PdeyRWg=U)CY{~iUa<}%+8#*(-xNmx|4d%I*4UMYE z!pt89UaPkH$85Rzk25&1@#?T5d|jP^p;qnqr`c;y8vv5rd=&+q~rN^OBi4 zhXY$8YPG^qdpR4?^w>R$L3;0;(@~hrm}H#lfu)E$u9Zh?^&8vE(U2`{U-`eQ@2%#^ zBEr_)g;TE1IOOd(8kZCKTMOGlcKBNTFJ&?%9WTx8N!rg8TW;8|=lrwjXOeHp&dLKj zbIvy?G|$!w8=wtZ6c%fm2J2IYb!@puJ7fdKN~OvtWSHW349tg3;~yk;Gh0W zNKQ*~xfjA+q9{+6$~l+fo9@!fZZ#&@fodXIX%);Z7Syh%1r+;kTky}DVx?^?>D;c~ zX9hT6%jE32jRVwouM7mHS~*~sGqL6iZM}lSj||h%I9S*D%gQi^p(2Z!V#>%MPFw7#yp!pBUvT1|v z-;sUyC!Eb>;$s+N5#_CrkV_=k-nu0Dm1mMwBS$;OH6l?PQ$0Fbe)l%8zgS=S@~?Qr7ED<~2eD60KYG#E0xyb)pw5{|`elQzsW_c+`5s3N9$(8flhd z$re_mCfHzbul~fnh|o?Hkiug~h7=rr+BUeEG4`;+EUl)oLqO=Wa9AgJ{RpeL!=-l= z?~tihHi$Bv?9g6@A=@)wCN7%>P9D>XVaOXsM0(I626_jCzQLPdm^MPXLm48{Zx4LL#Kd^6^MW7`5RwO9_vxKO{yNhCt5x zr9}to9eK=h2b;~q!p`=25GtebYYO%hE6X>FT$1wV#(d2Nf+@)iP`ugBtbv1-2rMUs z(W`h=)Ep+8HX%GnRMZHjq?cf@a-f5y(3{OxA~j$~Wyu100DnnOOTYY^T+7Q9s8rg? z$r<&*8UE2QSSDAkbDBWz8^XE|;H`5I-e=An5q8KXNu)V4qugw&!_|?_J{ak_EaYV+ zD6(UlMfSv)>={9{p<1M%=AT5I56Om>{=lG(iSv(jE5bk(Znxff^v^8Bbk}8&6?kVPtMWhhNql&g>=fSZp_7&zoQo?^*eidhw0dR6(HjeyXeBy*nS^aig{A8 zVDUvbxmC}GL%rL;)mHDudnFS6>`%(#jIT5rGr3FgnCqVQjmvk}wQ|qa-_&_8UNWw{ zYU+o`k_PkVv$svZ%znonM1Fchw3=Ljlhqv7KTDkukxK0U<1pOCU6IC7_{ z#L&^psv;{%kCMm{gn7wunHMwl{Sr6@^zV&GO|J| zzAiD)ZQ{KQpWDT_bkC1)Nt=YzZ#?(`CXfw9am0(%mRCY0iKV_ygw3xwJR;>&oU#4& ztUk;lXjS%(%8{-_NE+53n{#&>#ai-TpPHdaqyh^&j6TV#r+~3xFNc~>DBE_Imi7|_ zDHzIvSmioKY|clh*6M1XuM?+i0>LE`-n^(Qjmv1`6a_OrSDfW6%PelJzy$j;7?kmu%y#^Rx znknH0!c!^{7t_o9m%{zo3K+(0=uj5Emv=*W2Iqlp1M>Z3`-&sd8|_l`&#vxc_}(&= ze+y z&l}nbed}~FFAiaHvs6{yUkVmk$+KF-u3M(-$$&p3NetRTL2=gn7OTJBO4sgL7J8+d zz5lTb|F}3>#K9l)^8&t6l|2Hpn1QqWH*Q&a!*pY-boHnaaLl(7?QxH}n$c8gdJGM$ zncu0o)-g+hV0UQ%V;Ykun`x-PX}q!s98lTbX3ejhBCa)}V-&X{xx?uAiI7bP!587i zUA)lf3MtJZY9xBV&S0qxSFO{jtai`cwyOTsj>(e5WqUEbegBk0Yq0vfcJ?UrPSPjv zx(8z_sW;<~3%`TnhPw_|kaa^+dIosJ=^KMzBidHHU||T^ZN~oA+He-^XsxZ@#pOlE zC^sI(fh^^*El;RX+H2PJ=&X~ox9j^Ogy&b^L^7#iMP-)A0#-yr0zogZi^gT|{zMq}l`<8Tt#v?O&P4*fg1!3GvFjs9m3uVr67j^_>Fv@H z8+QDH>1kCEUzVz&QM17|2+<1#xM^Ykel*3n4w)Bg3;kC2q^$wbe2tznK2!HS)yzg?ksL{ z<037=QuAE+)R_tE=)u!{ROkm?CB12)O@He|n2vU+=PiU9JA2`Ll4x-!F@gwZl;;aNP;}b$Qa^_EWH`-60 zcskyFo*>B}v(W^^gl8-aresl;gLTfDt?t?@eNXqLa}RZpj?KKw?L^?iA2FNsA!zq3STm3@hlOepw~pBr$GBCDPhqZ936kfL51FzakcXpq$`yxLfZZn zcCO25XGZbfb3k}*N+D>#V!DYtYrvh)Q>|8T54WG|e4`Il|QM_g5CjzAn_|tARWFOEA%UzbH4anUeQ8 zupNgFGLJJpBHJ?S#y;AwZC6_$I!$EakmCl_uhUY!S0z`>i@~@V694>t=C#$&=w(?e z$=?qxA`C1&j=RFB$emp!z(hs+%Ebgz~9N?x9+zJy4I;{oM z@M+d9NKw&626UK@fdOS~%rsUO2)YliT!vt5*Z@Hm@;2MRg&_z3YoPPEJi9s-cgD2& zFsHNqzS7#(Z$F$ceB!z@$7;u~N&dKMh;?&^6E=4 z?cZ79yp-LbF3-6XB!g&t>79wr%UTP&xkT?7USr+0vxdeTR@1uKd=2>#Db#k=n9=)i zxe5rH$L^@E{;s*}>OEYV;VORuzAJ zO3ZedNox>PW8)?UW|8Xhhc$(c{5tF^Y4-3nSQnG9NU%=*_?kO@S6T6kSGhtd<6kls zRHo8s>)RjZ3954fzqv3>HkQ_=oF}^=Him5f%R0#DcVZE_V5tEo!19ahJm!Lf{g+#uwLMqsi8gpX<@@ z8MQC0H@R`?L_sonIkm{M>1}UtOLn7oWe7_O#y6z$q6=BqH4epoXc%g}OPO8;GxUPs z*MIKTY`D_SXTkrnMibMI@{#1Z6{o0qd!=Q^hDwPk)*z?g)3Psj(u-@*n{m`u3Ka8e?5+AHQM{UmxgI|Ysnl^ zpUR)(^@_pFR-%$ymD0GAp3%)0M%5arUxzzgyH&UJMBWsa+nBnOt8_?QohB5rgWkO! zR;xgn78}woP6o^4s8p!E&WDNuv}O8cjH3XW@~ETzgWURP5wA=%+JCBq{TJ8Df|KhQAa zf}JU&abs%zy5W&YfPFy?G&&tGRF?R0_3SYYUlk=0)DV_o}(Im{YU*yqB}#ioM_cC-O?ykglhY z1tBWczfF|T&fNu+D1Qu&Sb0h1D$eqI-IS$Rr#~zLq~b;Sd7r^n57*%aL`?@F9Z9 zU+k|d?W7n~RTex!%XQkJ;!q4UR?H$8Hg@P5~RjuukyqNGf9<$i;lw4Ec3pOTd-dHMFB&+Ck#4F*7ZyFcA9UN7XRaR*vUJ9kvgmU)47&WZQs`c!`08 zc8Ysbz72whg*;1hwg^bgb$>M#h~+h%b#nbq`$DsC>bYYpA*KRHSW;f|n!W3II?IiL zgckxYDuK>E4$~<&)^D1%@rbJ7FA)OiiZ?bPCj8$2=TmKB<11o@E*imr^J&R!)xf9x zi38__vSW;}=|hKO#b0`jw7R{T3)MFs`7a&LmTBH!ec>8;uwHnjl5NDzqxtl5K=Y04feqGI zy0mgibIunb(?)kcw4D5|3%$SI7cS^C@Hdk*Ey-PDCU|l23*r6(~O%18r ziB{(^c-xatYv$JjtJgl*lj}3IbMsKR!xV^PDrWVh8q7wL;eld%$wr=K^ZbCB?!Q@7lVjXyelgGGN1Sw4%#3C;gpOcrxby$KeS zmBr_f<#rBy>k(F2#;FUW!q+UuKwK2S5{SX93LV$=0&!8B_w$J@qHF}`d}DUw4> zWs47$EQT4d4@Xk1Lh)8cml1_Lz$X2hCgiB^ttRKHSeg)0ZJzJi3I%-mKFF&IT#<9~ znjWtp>CVWxmITh|QM4IuE7;F_4I`jR>Q$m7!y>quv&9Cs7Qi`;%~b~uB&5qo*mTMp z-hzFH3j~p3`ytrV6{c=bTkhuNAUSTQT`JE%l(SxgO2*Q`RXSL~tKsfZ3OU7bzkpC^I=Mn*1+~wUaL2nxlC1Eg5iC{8 zXayx9OHy7*6&7?x@M#B(go!Yg(?CmP^;L%kej-kr@R$-Q^TjLXzt$H74n;+yKW#v$ z+~eI4Qwuqvw`WlVp0v=tH|02 zw4+eZf^P;EtC8i<(F#j<ywNBo+{mR;qqHQiz9 zDMle;8$aJ%8$4}i9(x7NmZrj%3*?k{GmJw%5;K^VhiAxmXE;s9^e}F}^3zZ)D14?? z8D+rL8#{R94Bh-V8~zP8j@&o=#l>oAN)4>Ah`$IHUCSTR$}LyWYmZ_(AF3!ylCMj9 z{IcNbW_d1r0kTpaUP{bj44e@ipTVaqdtRYHRcbG%MtWps`TSc-Z0GLKQrCjREl!s* z_nn7PM7>3xtL^S&iged+K|Vb8k%omlL|Y<(fSBN*;kktzUkKg$nHu%yNRjWzHN0)g zUbcx&UOQMQ`vJ%lom{p|V&9Rc`%JS`4Zy&vQB=Rd69tLkLR0YZl5J$Js>(tX&Y|K|qry1*{MJ+4qLpHxZwdfcR9B@tZTPg{7y~zmX%%<#dyhA@lDRz z@Ni=z2vEw}cP)(2%x4fvdawfL6fy{5fg7p-0nux`UtyV9211o=r{aXuUxfR%VR#a4 zsUK9~I|VYEjw20XQxeq~qdo0QfudY8I-@EaylzKSNpSRML??4IUN!nbF%RDpe-~#( zSqdRt&%s=;*>XsTO%DD_U=pw=STVQ=4UPa+bGXLS`B+3;Oik?CLe3j?(Gjk%hW-dg z&-b~(BQJKex5HovUc`03DAUcOCs6JOtgE?$(9I{Dk#ZU5gMQX}Y=b0?bR9Wa81>>4 zgTyAg-j+RP(o$wvDNyj^M?hn?Sr@eU-0;)( z<8iXZmg9!AQOoGZUcEQ&lR{PtD^ogGzbM!Iq$sHQkg?W|opuhHh2)F`&caan7x-eJ z2jh#R@`?Lw`93Ghm581y_%W9#y8eyH0wFoe`r|v-Wirn~5r%5hZ=#Paty&=3eW^P; zE`XJXz%eXJ2sHdv4$l3e-$>T;fulY_V=l+NoH+*)BMe(@gl)gyB9XsaGC8IY1idZH zNp6*%_?bkSJ|xHA?D&2deO1sJ`3)$gZm?i9w}^xUCFK+w=h9MyX+`C4_B-9y;3ciq zvtY;q>!y(}YJ=p(G6&KVxx7CyumTb~OWw_YbFqtCpJG{(E)$z?b@ChvhCCg@;(zgi z3KO|sh)9OUxNVi2YErbO_dgDOlj>~e;$XFm!}nTFiS%>UDx;x7-z}3QzGm(#RQsAY zPwcOL`R7#~4}l%K0A^(`Mw=hL8O3{g-^~mG7u50ZGyr@ai&v7;905#l0)d$KQ>nSD zsh%cKgM(F-X>Qkzl5>HP(c{I0eCO<}Myrg9^PyfnfmRpDEayq+ z)25RkZkT*aVS0`2OTDjUdd-MYxrKA|FyN6x7y|I*+T#!-l@GgMf`8QZ2LEcp-u%6> zq-Wcc_lZN%{^MbQW!ql(5)D$3w#c`X+}wx}m{`|I&@UVeu}uWqHK3mk4E%M8=4C7> zRv=phJ^ZWtx)_P-DN&uXcaum-*yK!>`*jklZ}7*vf|GWap>$$R8#>gOgSp)2X8Ril zuctO8U2a_B%Q*RkTE$w1^8Qq3#F5?ZgqB3?yzp#&$X>u zXyF2;2Bhl4mQ$e}U2D)W+=Sh*Au1b_&ni1yq?{-S$Lo{^m`!5cjxu#Uh%lKz^*ER8 zBTOiIhs`4!_%l|9Mg-dAZ?}vxAZ!bBH!>@B~GlfW;vUAu%>iRw-dSWOt1F zLCJ?(QLc1tvY9$G)PVG`yNnd=Bq~#5=`OW0w zjex=t=SvkixTLCzlSIgC#?W=|?+3tu1WF>05>Fwhv)a*o!tr4@b>lW{u41h|ny;v8 zQ}T1LW@zHq(FDdCLrE00qaD-Qq@rRO7C~O;M|Ns-*VLG}A8eRO+F;zR1oS54k3421 z$mxWT5sj~Y-BQ~@7(&$qLWOKs!Cb}TWsrVfOiB#Sgx}tcWy;`O;CZVitBA4B5BFl? zrb>(MTYnT;auj6=gxG#Cnk zBr+P;g+Q|^lFf|d*EX8j;n-kNUYq^2wbwldEpz**5yYg-(~j_kFs(Yk_P{KX9sNY> z1PdY^B87TbeiE{1bjhic98W_dd*$r1qJ3tqModEmwH$XD?jCfM?cmD1*C-;{9G%A) z1p1H|ZLu3G!$h580xeb$AU=W?!BMis4$)TV(p)GsAjQh))l{ou-uJ+s#b{4`oQ955 zTk+EJA!G;v=_BaPF&tAbB-1ORU@%Glv82DR+#-q(9ZlazM+Rjh>^lBX1e^r8GC=)3 zc&R)ArR*tc$6`B>JCkW9q5z0lkOne4{3gcogTI^nhAY|h{2gOuo!&#)#mIkzfN{nN zbsJgxi#bmb@f-bj?QL3~ zD%VyU$Vz4i5|KZwKeDDqKUOc8|ItVf&LwdVw%T7yjp;XzJ;iH_HD}{aG59ne2Tw6i za;`tC)KFfA&?7^S{H%m_n82RaZ1@k~xYsI8psF&2QldMi&ASDE7iYP* zA2VGe$Ttun^Z9LL@b6m@uaQ*~oG^Oy&eKhLZAXzKU%)@+(>D4QYBe|LeOZJRpC|a z7J^Y_-Y8S1v0hTCkX4xEcS~EvnC>S)p2`9qJ`4?^O>I!72A{k`YfLKX z@R^8Soj2a)U=AOZtN?=1t*|Ti>Kx?=;k}97wuXQGS_oKGVz}Sx8s_P@-OM80ZKmI_ z?Y_>8O>8f%-8Fb#g9wfz>Yhz+s$eEsvro{fcCBd%nb}$M{L-0*Sz9mg}LXEN)i>7UFMsp;t^zKY) zCf*g+a$vbd9`>NL#9#b)*0ap{>H&%C)&TftwFZj=ATl)hR()a|6_-WZXyXi9wYU#uR_vf~w1FR} zNnNgX~WB*$cjzjd^ zLVjk~P=}c0&w*KHCBWCW=SHg?;%))fw-GJu^=PO@w89Y78c5)v|-Xm}fUs@}o0I9;bv!vdtFC&(rjqdOh zUb~cR4kvuNyE)saj7ITVA~8L?6BC=%A&LwZZrnBfV;f*2CpUd&peU9z9 zGzGhi>Pt*?oWoB~UW1Zv2|0!R9W_XXgS|>W`oP$(hv3&&e?r2Neo|T+a5vGZre*R$ z(t4jI_gVP?i8?i+^MSvIv!lWYCF0c;yPr{U2ag7zx@Kw|X>USzw7=<8O;gRzDO-~PMT+lazsYF2-j5tr~{=C={#l|;&{OlGS! zEq6I`Q~od7-ZHGNW!n}F4#6R~L$HavySux)yL*B=!5xCTyF+kycZcBaaA(%q`C5AfYlO`R)0?_))I^ zU_GE=Ve6J?w43D#xMFZkZMA1*JMS|m47RA;_kEX>BhTu5=v|g+Gk&bDarWwoRbOp( zVzRs|;VQ_yrp#hy`OW%sDs1cRhK?;d`=WG6De3kZAn9Tiv4qCA89xX<3_TAcCwpNiNFe^&z$8k0?mY0QoUZ-BL1`jKmxPW8uK7|dG>5FujX&XmvD z%d69SB^!WHW#pBv_RnPl5&I4K+t?&L#!Tkz?wQFACRw4O&Uao01J_uW*D0ZhyqAD> z4GJ7DX4?h0@$ggZ-uFgdwlG^UEBB8wI@yVg?>~zb3$0f?jCz7;mUjH;1bIIzVnj6J z^I+_z2OBiT5C}?_mb0Dk0r6yM9k@-xrx_i{NEAuE2zRlrhJ5!Qz6gt!O`^$WX8I~y z6)G6Gp3eqPVx31X{3L{=_)sJW#3d5}mrv$2yjTVYH_i7!av!wjIxpBjdcfefZK&9k z<3QRspBnwYs62L&=iLqQYNfeEnS{D{i|>v;oi&d$?)E=4MGL7!fq9OxjMYj$slV1vPAu~M)rGqfn5r%zp&5}OD=Yg4+zL#X&BbgI z(W*Dku^o-K{&fswsCK{V$GDo2qB|BET};;V4f_M)?~ZDzmJQ2%M={dXjz*=eBBZz^ ztt0Q-?ZgDTKqLPIyQ_Y?>vi-rd6rxB7q%s0Fr`vhe><>b$-JaIN5%>6S&guePhm(P zvR-}+_b~^5?~`X&9ol=T27%MgWkC#EQa1QX4D7GcWY~8Xi_pg<3?wCg_@^GVl%B+r5CSeVc{pc`I2$K$jPDzmJ+fOiyY9Evd7rm- z06h<}%?2pJZ(6L@``EW_PBLp%xyPpdtG1@c=`WiYh z8ca4tJrV6Rt{2+KE7LnmTVm| z7-%<6;a8RG1|_fj?u~Nn3dv2KNN>rOtH$9b*tR=V*%{7U%(@=TT0ZDnHl&#p86&aq z!uaJjAle5MK!jjMzv#N}XP2(OeOTm&fr0s*G$?5+)~+5Btso=(kg3v0PP4Q4Xc%eD z*p`W?x}QZzX;vd}iH8vye-yE7z%(UApq;S(Zun|Hy~}`Oz!dRRuGoo@SW)UR=FE0* zk9HGx;(%hkUF8CJA(VzrQs z=euqWCyjt?2JS$C?Ax5CoKgtu{BmR5UwXB>`MYWUby@Q__}b{LSqz`6 zC~l`IcjyB@Hj%0Y#qe)3wp-vw>;^obqDeo?r-GtVn2m{y=g9rUhaI~Iwkr?JcXtrn z5arzOk3$cF{A60fS-NnFq)v{PIGcXZD|0|t+dGmJPN;{5j^bU7%zX1k(*gkW^S+90 zr<0D>xNai*q#}(T1=wEVUUTZcY2hrP-2n{{2s91iqSlXhh5K&T74{R8i#bk(=e9>g zqRSD1^7@-`rl|SC&y>j#$=;R-LjxByzfHxgr4k_M_DeJfPU#m)DCFBK0Im~WKnY3P zPlcQpI5H5Negsz+IQlQ#9LBS7>!L4cSDMN!KoqR@l~q6MjATvJ9$_OFZg^XRBBv$J z;1=jb5bV}$&83tqKyoFP|4}BEMxFhqf>MZ~T^U+J7TG&(4b}n>#-e^L$_9%uiKon2Ls1ytJ{J~IHMlO`=dXV9;DcVt&u6}%(1LVKY*DO+k=Ibs{mntyFC!j;+WEI zXU!|Q4xE!LP7Mwt+hruC21TatuxwjR3nOu7*gg-%7AB zc=RwDUxo{3NXU3akx`NsN5V9c$Oig5{mp8Vx$}|5BQn~VWB)Fx`ENe7#OcLF6vwwR=jq# z7y8U@m~uKfGJ^h47n_q7ASqS|w-owNsoD0s+f$iH;-AU7;Z9mO$it?r-2p?GOJSO(BL736DI^INxYx?s`lJ zXFJJQn4DRg<%4okF%S^FZ-2 z>hy!|>`VwcD62I>X_{p3aOfMm6TH3oEd2Tk!@YchlY#+zTJ9z#xobshPO=r+EA0fn z@Y2QbbEcyCE#Y&kiuAk@u~ey6_rW46M<}~(01jKN!%Zui?M4C!R>h9A?GPCoGYO}U zyR8WJ8^bWWbaYB)YTkfYG+HfVUBi`e^MJlQ2#!a3rUiu7Dm9!}C4Mlx3;)^5!Vt=Z zjgb=k$Rae5v;~OO{)-;Kq$Kj2OvAu885ST7i-ODT$LTtZ_LsXCs7u5mj5PPJ_+e@J zIZ5W>N#Tbv5!*#2bR3Og^M<-MHi_g6WroItL8ap*U>bGj*p~sySPwGOs2eV)d z0Am49_kd5gwD)|GF)#di$|?rY^b9nZb#Z=Jt%BZ!fb)1y3jGSkxno9D>L;9}0AOH~ zv9KI`>8Q3(yA{EDqpYVNCt6$rl5wVPA4R^^QQn_+NR&HVKo3;XLslA=m|jd*6=A9- zJ`4ObpNNS`Dh<7d#lq2oBDO`eePCK3;QdX9{->V_J6FUQ%km6sV$>G)cMw(+BrqUR z0dW42Pv1?J?F_<-$P+A!hc9H}abR@Bu}#XHJXqq)9?mlR(JkTeN-HaBH#ocsS4{+v z0)D6v@sOz?5-}}cmQ}vez)$)yvWG>vUh>4P5auL+-c$8UI6H&DhIR-2{Y~nnUAZ)B z;?G;BSm>u~wypje%olAFVQI|hA8*8qDNNj+;HCa{z!w<_u0_!&3H^!1#kDmh0l#32 z`km2rXsmt_YATc;hht3ye4HWxx*a*j(G`b5d={HjE6P3U-etLZv~gC>%+#2ega^#x z;ap-C+nn)qdn@JpyN?dN{x{aO!Yaq#*u!)ey=j!RZKk9gxYD-tZB$0XpNa>+K8xg1ucMNdV{F0 zDEOv$fNv*sXRK*{Q9cm~jZ7LP<05;{Gy{+rPE}CP0?8z21OT~|AOL)kKN0S@Uw>X3 z5cSt9y>ZP3zQwYu3L>-4<39wc>6rNcX+i5sYRDyMtkmVDn;6J4wyB!bEasuvTxR8@A;Y|cAW11PPs`F@Ib zD@6hf@#<)KMK7 zQ3JBia(ScLB#_4<#9N}t%c2OttJzU%+R2pi5G#i#B*jdFQ31+B15$KJLfOgMTRX%R zYMz-sSkX1eK=b4h{_~LkQOb$^t-z5_udTJjZrv9^qVJFSlbD4jr1Qp&Ns>cG#zaj; zS(&kBU=uum5A>BK;UVX%67baIWK^8Z_rMwgVnlWTA<24`<=~5-qN631<*)|-HoQN^_pg!*6-pZ)1zmdOwE)v8XO<_} zLf!HuVMvh#)I#`Yl*6~E%=&id+Tl2+MdSP;5+)jWEDNj|X89a{JkJML;SUhR977Q< z(q_>Y-Ul$qzdF1>ah`t{9(~~-Q55T6K@B_~Ie#_zkd1Jd5G%#Qw^>_&D9{3^v?f$Z zKpkrP-A$7+TF7`*j4LHXL_LFX6L$B&+vf(c!1PM|9ryb<>(0ym>L~sMIsaJ@DUhK_ z{EavVpsv#2LTMX zs1g-A+j(m1>Iz&o{I)6nHt~N~%Acd>Hx71lg6xaaZ@wMhPfw+nh+thc_>-W^kxg=D zSRD0bJS9KZGGjT~N9E-~f<3owB?=^NpO#l}nb=j$Ww%b!|2;+cr_t0s=nJeZDBQ0Cz6S1Hj&wMf_=314Q&}ORcf`x>0o`zanoBZQv|5q8BfXv7^y|jiR zhr`(kq>gI*l0e1-F(es~nF*qT*9OjQmH&L}d ztqbK}5p*g*-(XHVec^2u4Z*S=!8HAMUjzqq7G-RP0C?mpE~P&W2B3Vfg2cf?HYN%E zhU0`EIL5*`_MbJ#aRh25TvB{{Yi%*bYiepb+QWt@X&;5~_W<t1FGiIB>l0S|Q|~N6L$l5Is4!B&;#d9aAH!2tbj}tznJXdr`>Fdtogl6RhE)eQ zw~+YwcAxRbBRm%nK>APKqQC3$y8Hk8iuwPcuK(ZNGU%<<<-{JiXJ-=kPwV;X zeD2h4qdlza<6SHDFOU0UrvFt~l0yIM{NE=dK|Xn+Q3-!D*8Tg(KSxHBCfBo-)1xDE z>y8D9)PjbS5Ze*VM6&-jj3gcf=zDmPevc1#-^CzII;rs^#G?3@ zC{0lCG2fLaf|-@c<{P1U-AaG!a7tZp42z}GuS4pAi^v9^FKZX{5m~yh|7s%WTMh*O zT1auz@48qX4N1e5w>d?0>8f;)tr7Wcu@|XGS~Dre=7+FMe9~sAw7QI2yR|)T#ReU! z=%D}=4-q=@3A)kfeLQ>Ok5FfiN+#`f?A;n!9s2xiK5m`;RE|En&)uOxE!II1RBP$= zLWlV8N1Huh69^;Tc(ZTj+_P*fa4qrr_9g?F9UE=a@c+@#JB}VE8t0AEAEIlS2>22sfN1ROOlhAsS60;5x9z0Th z{c1VcxurM@KYzj|bl7RckY5qOs3ziJ^Y~n``tbEES@JQ71c0aIS0M9{^axHR)SroIO6q*m5qNp$LqQ0GOgI*YWFfGg4_SqS17jNg~ z6x#>)Iq6eJcB)GCee5nSm%G6!9n-emc6$nKl~&ihO}}vGN>#M;MdYgy7_szf((6MJ zcIli=aHGxJrJ)|(8%s(+r_Z_QT8r>ki~72|#hp*bn&o!en)Sm^0IYuu4?ui5klYt< zB;~BRt*sYGmWEH)wwd~;Gg8n10?z(5Fp#WwvD&PFdqF`#)tgTXrivJo7N8CvFiVJY zJ0T&%1|Ut*eIja8?uaI(Ht<1$e1Re~HvmV-Qg9oP=yL@|5Vr>Cg|p-j6waB$Lep?Q zee@V1)e=M2{Qd-HLCrvf&OjwdZ`!Rdt?evNa2rb}5f-{h@V&YOYJ+8xoc2$}>wXQjIC8ecA2)fZu znJ;t-cJQT6ji>;&;7kMf@tO$nb6_Kh8qHfD;LVEUz6?0kd*dJg`A}d&_E1R8mfLwV zatpFrjdot2Tub}OZ3LDEE-Z})*QFo;z3Ps1C2FHbd?w`YO%OSzhNLyaa>hepp1=os zFp$ERETIrO?K{DQf`wNQxnD_tNYfSmo^=A6eLn!g1M`Bo*FJD(Xm6j*2)Gd*lzoak zjXn2*!jS3P$|9k^dQk~|S_FO{(jdJ+t8Xuc@eiM&o9>_6Yqz^>hjZYDyUQ_Rcsj}bzlrWWJF z>nJB=52jUaJxuU&t0$#pomEH2!3vDQT^e&I@E zDb=6FCLgqb)ovsh?AY{ILQ?}m(eY4#w%J*WJ@_Q$7^h*4QCUmn1H1J)c3C7w6%NJ? zuW#6k5WSDV#%gl4Er@ES5}CDtu~BHGa6WU2={3@K)ZPt5A!!gp)wssVzDtH2)ehD- zWpID7s2)R~#zN4za}nfOND_U8cP6V8R3^82XTC;*Zdrum86Qt#|HVV=Gl|taB$IRA zMjjMo4{h8a4!b8+=?Cp(gpErDGjdKB=n!g%23#I<-2BCM67OqNy}Y#tP>g|ul`hgC z3j&VQ&;4T+L9JG+S+^eM2bah*4F>(2_z`hPEJp00zSRfZl8Zaf`fvnYORfB3|LCd# z^Lw9gyu6;ipGzHw>ePY9!oY;4u$E2Oj*!0Zp3u;8=y^bM; z#y^0_v*b_1EZ0h>I=pM~Do0 zO@zBnHGY1Ez}!&xP$V1SO{F!;fm0S>#`TxabCK#b*4vi|qIfWDjZ}OmH9ZFp#8Yvx z<81D=Tf?P(5^B@#TalMO-8N9(AU7~V@?SXEsG}RHJ=&l$l%Nl8Vy&b}tY-mLfan0f zw-yL|KU?3fV`LyMf8b@nsuXa>}>F>(i%?TM1zSWv;MDe)lC@Y1{+Yyx#zrZRj*~gSnEk z6;Eh?=f#~uXzEnnwPtO3N$Ov`2m;cTnX9EQKbVEf5k#g4?V3}yM;&0v35{bI4Zc&T zANn22(y?If-oS7&}(wN=>;$2fi zhbl^wrsORLM<|#TLc=EZcPPd@m~uL0qab}eC~!8MZgzISF78K~PYDh0Q}_^S7`%RV zf_d`5A2{^BNZtrBGzKunjJ>eI-9&=It!;mpP=I=Q{i^VHjdnsqe2hjE8KU3ij}7Re zuIA()&~Sc_A^0R`qIp7Q`=xqMa*S5xDIY*H-BMASnH3>8)-~ep;CLf86%I7(W3U*2 zq?L10WggpLZuT={D{2IL#Ii*k(vyriJ_rSCZkWi?!@zgcN<7e-y<~m3i#-sGH;8?> zeecF9XML@4)h(%7H+vRRE&( zBeNq?eImgKtcS})m`tQ&oiPe>Bolhz`?9Iv*Mm%yr2FjT-IUsHF@tJoR5H^6_yGgC zdOjwC&8T6auk|`?Y;&rjup+1d#WukYE>~9rNO`AdS4$`=uN>CiqR~My5(_95lJ>(q zPhq+<3#rfw(Irx5dYB>DOR6&mi329Z+HY4q#$C9nhGUYU-NEK2+6Q6Zrr+&Z9##On zm|Sb_ayzV|dwiqvyF@7*6MLb!QGMuIu5p{8gB)7xTbiCdkLRg@ZKb261}Eq$_A%t` zZJp2rF84`C6m=lhL7aB124_5MFbqy$Tbtt;ojg!RJDMkwo-NMT47Lwm2-r}LFpCJ= zKU(M1r3jqMOCk>`(aThE%;>H#zXW#6s_N$V=l?t-A$N0cU|vf>VVfB6=Vs(XYsm-Z z@Y>uai#<~_;R!`DcJ0#`yg}}wXpV!?H#RM&SI<=r80IUu=Ccc1(;i29|6^&cj0som zq0ioHq~7CijnV{(1F;BxSN*Q`@(}X7xB}t&8^s+1I#W|0Q3rdi@0yw+lF^*d31pXT zOQ18md#;h!?maqcdW@oI2*OSZBcN;tCdS`5a0%5!`Jk_@p?-Y4V@pRQl}KW#GjDH1 zI5n>FDzI#%necwK7@lf^T6F7B(iH~E4m|QoVA|zLYEi~Hfw6Lbxr(p70&Yfp%|tP{ z><#vEbYC@r#GH5=2|;~q$0$|G&F`^fqVDJ`pGwN7^bjkYk#lIbTqJH=GGe(BDJZlc zaN#p$yqpV8-8e%*t{Z%$y;vWgWVcE{#wgLj`tXrrckZU>{wv=1 zFY_Nkhg{;8za}sy@weJYLDwknVPSZQ@;i=sJ|DoF>7PLL?U>JuM3LbpE`;j7DEI_U z7cSV|;Imuwi~iRlMkkq2cP>=^=$SUIVN|UPvryf^NHIi~j5H&5W_VL>03#lZ7dpwM z-?r$!cf0PH-cL9h1uc?#0Sf;BI`6$W=8a2V)p>z*q#!)8#48l^CT2L~(p_rC^SvcI z8C>*9XrYXm<5M2nTnWOk3Ou05fG06q`OVzC%DH`uCtn?w^sb`!=*^yr>tdO$ziIi{p3aNNQDjC3gW53eW)#MK9K zu9M151@MC}2NFB0ZwRnTtit!ObjJHqKW|~jI_N79nwG2C#lXIDzEJFv4pzt^`E#Zp z%gcld?foK*MA{ce$E&a0y%I1@>*=n*E~UytSE)*p3jSmi91YxK%|Z$e2pd5~dD!TI z{;l+lHRH?op|g?ZpTBV*Y)j4)k}HeETC zu23e+qB7Ko^$4PtCr5wH+XApD^o)9vfuPNo?$0}x=C#g~dOPldCVw6&mT3km%^`V@ zugl5`6I=g=euU2o?GL$jV4J1@G0`gl?fFW(?z5d&KfhF=WnhkGYkpi1h%sj!mh_uH zUne${dE{Af=th+vqV=#5?|YaJ_ZIPei$6}gSd4#HqD?pRZmfbRU|axqtsoNi34jQ( zCc(&?Ya(Q0hOtgYFYV?~+X?l&!DFnql ze=Q6CnM7c*&Le3vO>2u4$p{mRFNTqFN@L(BY|M5yI1z%*6>v#L!g`a@^!vm3UeYfv z)8Y-L1bxS3B?W~cRfo(QE0IT#9Vr&sAI-^N)I_R`c`-U&n^&iwSS?ms)s&cK5G@dC zUo9J;9!KL>(T$^~yJzno4G?K{GI0gkyVTte{m{1In?%(ObE7(T7;sSWJWScTT zOs+lVp;Y^c$utvP2FIcB?o#vCwr2%0jdlrhbLXiTRhn;1JN(z2bhdanzig)yBR>tI zfIke2VUf);D&*#;(B@6N2ZSoDgI%lOk3>37cx>E-r_!v3B(Wm(!@uW+^a}aKME34z z1nVcpB|NpZ9@scpr;||IkPRq^C;T#xrIBr9w zpqk~e?yLxGv!7RTG6RdvUEZPT+?!My#G&ZiOStwggo!0Hk8}BVYTOf^ZtNJ+`}?86 z;Su?d@aLzU?*zLuf;QS6F)&7<#@rkyPPiBqIR{2=JSWaC@d(00`2-niVldVmo#)7jEnR<< zS}XE4KM~qD{%*%#|4vhCbT+d9n>EtB^`6kA(2^0b+PLSagMQu)gWrpu!XcW2IU}#Z zKMDDuU6z^@7T%j9w89^^B*aiE&GAHqXd?UC&wb(9KU#R!E8I)uu9r|S$AKKoVI}4J zantFi-WzVUoiV)04i&umXK(gjCDG&_D8A60$^>!TL!|-yF@?nny;85Ma;zo_7;%%% z;(q!LUkj6lW(-}K+FrdBV3nb>zO~$$o#9|SY|Ph;RxG5Q%TPjYB!w_>dBlj$n zN7bBeZM60bEo2c}JFzfWlf#9LgI8t5?y8K{K=_{FfLJJnikLT{%VIO=1D+&($zq%$ z9@f8u2v*ZfSRfIXD-CLjKy3VzbL+3*QIWC4%yoZ8@4=j5Bo+&*LaDCy;zBQjQ;+nm zRa_m@gMNK;Ct4bNAQr?8IFX`<*t@LLe}LD2jy#XcIFvTU(bch z9Xtq?(ty-~w?*Ty_%aF8ui`6F0iq1Ww8eUh!4LhP@h|asP^A{AyZg?`XtX%_kkc#L z)0ORmR(Cg!*lZD0KTyECT1UJkPA4exWZ&~$P*o1Ku|?C3(KuR#=CeM#ZCK}~oVOI3 z)0xl;GcV@5VDsc*V>C7j-d0u#fyjy zRqp_IddK+mF*xp`-`2ZM@qNT(t-UZZEtCmng#Jvs4&KH!=!XQ^z6*-&PB5b;XX2j2 ztzcc_NHH~tm+Qd-3w}g}R#Yqb_PXLagl#m_TB~%Qv&#ZFvbf^6nXh5#BsV(Ol$3yx z;tc87R@9)am|mpxTxlt8$g#d(bRpek!nHGd17KM+kr$KU=qCVIh8JsewQVmKA=ZJW7Z8)4)6Cqi(6%%~WJUo4|;sCm{L ze&qvkEj`djXE17cpAs;w!-m9|1F6xu8c%eUfE}Kei1#E54QhjwrIA=3QpM*9DtV7$ zblsUuIZ`4}oX?rZBdVWQdCLhIO`?v?pd;t{^slasMue{}FRd-sOkHjGJKeNa0#s?D z0tyWCSq(Y*$Y7AQ=7}O^TCn-SOt#${&$+SeS^YT$HeAio>{KJrKM)QCx$&YJr4fg088}B1G!t z(N`s#h;**<12HfaLqIkFHuxwX@r`We)#^f+Xpel? zP;;Y0W@GiUM2H}RPpreYC$-9K8Sg%mB-Vwa6x0*@8U^)w(Gfw z;#9zu<&T|?L+q5KFwWqB?V3`!uMpITDzg}!q7M~cg}u?NozIdmaq1Dvm=XE6J_%7t zwkkPS43;n=&DHER@m@Btt@iH>ZeJaq8?~&estc}j0H95oCYycNWWc7xAgH6 z8AS-|n=CG|8HE0+po@I~)vGAN_aX@o%n00e2E#}P`WOls6ayPGN5LhxpHKgEsf9qh$oo6)0HPLz3WZQtlsGaCj$^AssKAa}2sSK_o{fm;E2vfG0% z2F()7ErEBa4A#!jzhcM;bui>}B*b>YFw36}i2c=$TCdg_gfk(8;_7)YdwJo6<|qrc z#jB*2>M&6^KQkienls|&d8&Kq#aEs<)j%h&=yg5FFgA#vc=*W^#RMHN{wlid-*Pa_ z-i^)g%KS=XJ1RYeF7G@C)4-D$3WH^~Dea-Z`Yj6QzE3~3$0avmRO4VMr zD24}+-CgO$s$h$4_H zG%|)%Ej}-iffRAMY($?MlYQMz@Vbd08mNFqs`hwy)u0W!ql_t<(jH<9y-`W6gd4uc zt~mJtQd=IfUW#256Bx06 z-k5J)GY39zz!u;Ez54CC3$T$|6cqkyo{Q9?u)e~xwZ72Y7nK!&(Jl?TnVaXLr9E_G zN^H9gy=#<5;BW9HEosxs*0~s0IsR*O4zyzm7veVh$fky|BAH~SMFIbQjx#nw&zOX- zy;4(l0{c^^S-YRXz(eM*p_7ZK@s*H7;i=s=G6CBDp#mYo!kF8H_q5_r{h1Pwqq5ht z_zR+0;z+P&Y4_-iZ#5NB=Ys6b%+MgME}>hMK@8&ZI-|Dh0rX5&TkY)=YKq{3W@46L z1}qYxT2wk#rEnZ!CkI5vYX0|~iC;o4=`e~VaR+vr2;d$>k*%37V!X5X{@a#_0bZoJ z;hF`KiI+f;K!%~hFE^N+s68DOL&OR_s6w3?VMM=c8AXPB;5YJa2*4E;C2!ePuLhPp za-gf{Qp;3(z(VNL{ENeJA27G7pwfVr$nf3@;>LUTHu)hS^LAf}Pp~iUi znyWF#hOSx5V9NFM>y1^wHh*!t?*=A|aX*-bI=b@t9w5ei9*NX$ZorF9-Gus8^h1MW zZ3U8$x%~2E7=!H?`$^k#^4FSdZXQwz!ji=r2rv4sh0fOOLi3}_+CoLJevDvN6S0Bk zq>F2#0Mbv3R0=%1M|83!iJcirBm*bVt;`sqqqSz%QmS26vr(W=CnJDhn#IAFuzSFz z1r(@m@HwyjUJRZtxYz>uIOZcRjtOz7j^@aX0)~J+2p+$5+NjzO-@Rqz-^=}Dl_wGb z!fL8qp_wSaT95|YK1jpg*G>p`aSFqxrcx9K+$6^F?6FfvLN8AZ*ru^TKHUlE_UM7e z+=7>5V{0zuIsi2PlJVr*1TkVT>jy54D8-S)B`D8#&3`R}y%o|;clx=}H7_c6gs@k} zc;#(2bEwynefgdVJ6*Er&K~^0I>Cy>;KyJ%k2hNQz8+ptl;Duc)~-pFXX=q_M80bi z$w=8D2q-+<7@u$6Rrk|@&{sap5YVs08z6QbM;XIfMBIqM(Tk^LS>K-bVfQ}}(I37g zx&(;JN1A`wW?iB$-l5cF`CeA5Y`TQ-2g)MyciqphnAL7hRNC6xol!Nt%y)tJcF7Ca zNQ5q7mqc2%G}tCukrC`(i^n-Bgg&WZVSM)3C|B$DvRy?^p^#|{rDi}Az8t4`#Q2%7 z6unDW|3Il-9sjFEf)6}`mBp*P5x)4s%Rgcp6H>~C-EhPH_+0Tz_)j18eh6Np4Zwyz z3+nY72{&j?d`NFqL)+CRZ&))z|0cnQ0cykI$5$qeV_fZRCg4g_>OjAIO;%;m|M!KN zdGnlTh$k8_x5fUlC)AYf z^A>G#k`R5VnL*`0(gbp$;&t5J-5W7sp66w=a_au12dE!A9!(Mb5oBa{JIBp(-+>uR zWrDJ`g}CsR6BqAJVKBM`ink8U8moW!xXlvDBA?xDb$Yt08KB&P^1=~s#6pRZ*x#u|BQd~v z28B`M?)f*}c4m{%BZ?%z3S60ud_hLuw+FzHc>8Qq!GlEXB&Pnu;(@0osQ+6!&0i@VwNbLD z$XHmZM$mY_E2;Va{eQ+d!yy4%T+2#);I^SES(KVwjncnX>sq_zJoLIs*6Ht)FOYrC zvTB8wRTKvkitC^5(-)pkeva;4EyX8Ra4S70B%QGCJ{#Qnm~d!P_NMb?t7Wx(ls|hW z`toUY)k2oZS|yl;9tp?;K&^rKW;JdXy5)LZypsl-oj&4HK>?Uvm3+Dm|H zIW$nC^Y&<(WHATW*!B(%3c4cyt{31D(Vr@$NPmy6&ViS!i5h))smTk|-!(&_Fbv=N z!4|J?W`}mQnRSOyjPVr`wfq;!V{{>ZyE2s^bA?3N@EismB^E}r=;G& zB$Nuemc}YrrHKG+$mzF&J}_%m>!oi%t;g->yOR;ze{}!3aml{>W|4Yz*ub0)r6*R* z4L2I%qJfd#QnoJP^pV5_vsvU0QcSLMW6D`!#H{?lap)w$F)(>wZ{o5B8`%qV)RKP1ZB zsl|-ug;FJuHwKoWFgz`SHK}Ex5khnJa>L^7#VFfFv%`CB-S)Aa&fs@kr+*y0IoOp) ztlp72DyIyVD%#k*!wiMn+bHS3^O^qzD>zs zQE~p!n7ZB{-FHCNzj;2X8r^N-a6bCcUJgCxu@gp4Z#IG3+|oihiJlo42o7|1NMM#? z47bhM{mKeZl6Y?rsQfh=^OrI9i#%x5%Vu(3ll<`o1t3LojSyBHo|~H+JBI~RNJ9UG zw489~_ok+g3TY6;mW~(}yBOs{N0iLL?Dx&prNmfDxz!x_4L>$G-}%S@URhi{f4)3c z;^!5KHSvzJv5G)T+PiJtDyl5I58_tVu@|qqy_57B=NrjapLgcUuR5xX0_=H?Ph!)} z0&49`cPh@s8VXrf6IXe#)#@wJyNj|!3d0*)r|O7zmfSF#2vtTu0jQCr#5ny`el~p@ zql2+OJe7`%eKF@pi;C~zFD;v}ne;A|3nMM!3~L;B*+SW|tm>mhZEdJ+Lyl`EY~BP_ ze?vxQy%WhAn`v;<{R;4wmO-n}1j=7qmA918Jm#$q?46u2c+F3SwsuK4wJ86O$@1CI?nq}OljQR0i-wc zc)(m|Sl+Lg{cf%DVQc0z@9wH*7)OF7GN_t&%9eGmB2#ZZF1EoQjn+kkvt$P5y+))Z zq5%6_+sDCKI`|=ab)_TP0abkJ-$gL$LEz*beh0P$SRO7R##-fFG7U1*c|C%O}bq7;pm)DIyogfAH(_TzR@)fkZsSL zQJeQF7f^|=WDgd+9e+4lIhP)mkFi`NPeBI~v_{;_ob)$_-dv@4HE2)TNUZ$V*m~tl z%s69gY|NiG{T9SbkHJrJz`@S`)BEY#@yGa^>1?5lz59NmntxwkqhFNU<(+N}z(~Yw zwb6#%^R}4#eT-@Hr%IXXwY#nG&(S}io|k)@xl$Eay7o&@ZS8R0Yi3GH7@#LMJMM@7 z<*=HP;rEE1{ozS`fqy&{84Y6HYg^5W*W&`-GqI}=V`RszaQ-*D zq1-L{+PB64VjfCVmh9UQF^C&Hlm`U5?(#gbgo5vB@SybD7DA1mWE8*nn?48~y^Ciw zJ7H0#{@$U$5|`+T4m@Fae36F(upLJXIvpSBTq@Cdw^~p}YxBi$9ZNiSgB|Pq$STp9 zyyrQbs#jv0V>#wWUPXRABlK?bqoVz)BVw6UStjdAdxC%xzhh763h@h3cQ9|9D_6E9 zoVI<*N*q20|lj2#-*-(oA!=dsnbqJ+Cfgo z)o7|0Gb7tm2@emkK@0}pzE5)#1$jdg(_rL?lY_%wYN{UOn|kQERc0R_d5;E_i>l9@ zFU|=rtGqKnnmm2&IT#@F7OA@_kDDM*){*1F0Hk`o#f#&lA zPtIQ~z&r!Gbk+c0k#GNqPSNz`aHV_SSm)7~gSF43KGHVZ+aWAVZfh`Aw=c*7JbJ+) zWw4AuI-pv=7)q&6iKQ+go8&Z5pA(Z@idWV9ODFT{PD~@SANWo?9TZ&7_BRF^->yIw zbXrJbR&$Uc;hsTw)*=Jw%MHp-RLKpT=r=OE#1J%xCYr;+(kF6(o}f3p5m+H}P{(2z)-Y4xT+U!}EdVka3!mg|_KSoA6(^w&SI>jbgcru5>TJ%e#y zTxeV#(=xj{4)NO=?XulD9hY8H^0x<cy+Y1=s;s0~$Kh|XQvP~)p@v~Ue)%X+FrCN6FV zx?1o@qc{6Gj$I@ymY>hi>j#-3UJ$Z+vp1-##k(y`*+(J_NCCB~BDW zjPn66brw#bnw_^4zf&!sycXZWhy-4$d30Yq*MS}$yVyb_0aVZaL2xo(M+;q2J~TF# zz}FQ4{&g^hU@(=*>`(r2-uB*byy$#rpCeEwBdD&f?iH{0F@x6M_B>fX{p03FV&d@lEB zAl~Y2W>-?^88iYcP@vHjce!lyLoEFvIJqNpZMwt$%IY%v6lQan=F0Gr3iC$C&%^Wn zyEJpTrmROl%~z}V(W9#r%jMe4&qWA6k(2kQiVzDak^}GS*@UOg0@3ZNmX_eAGzdzC zj)1WiG|5#48j8VEu*)+qBKPUMMn>`v$x+U^nK)I0-?gO8w0IUbV+}Pzp}Z~{SWp=w zO+B;lH=CqZK%=Iiy>DhLbV3VbqdC($XSL%F0XJDgP?>6znviyknJf7j8P=Hgy=~Z| zOKqXU`3R+DDF^rW+?4B>Rh-;zNB4SYbJ7E+?K*HpRTI0Eo{xYre0;E+2MyfiKwU7q+@P2o=&Ec7shEI9M zS;L|akw(NM$M54!ZNieaP((0pZ*LDb1}J+S55+*6)aLuf>3Y5v2Heo{*mzT=4gVi# zBhq}W^mBS=KF|;R4=F4rGDchB!FA70^nj5{^MBqOu%?ZXQdOSryB1}2Dy(FyBEYkl2SbevwTtg?Ey>^$&;AP&V+^7a_KM|^Kg8gWGtKBN)w~Cc-tr9}1tHC{iHg9x$1}?b zq_kgIt26G>!zpAprQYL*MNuZ&e37W7PKwKC3i$-Ez2uu#VAtM>Ick@q(AwDHM^>e+ z{D8)0TNxIMS?ol3864S4nW>+??`;wNyzj3~1_T5)i@0}C7OY?_uGl^r zh}yk%h7u9)X?QbTE7ZHWP29A0#OG2KD1+?s{;t#;R?MtX0p#076pztP;>=Z8{< zC2-sHRrKSWf+V3|i_6{{(s}KIqFLbK*{-fRykAybOv1zchqO^KFhbhgF8&W&Zy8nf z7kzsh2&gpD-Cc)nkwZvIH%NDP3n&~q58d6}-Q5k+DcxPqm*4;1``j0oF?{7vah%QG zYt1$1XOS{6AYNZz+o>swiHUL3g~nrsHwwW_KwL>9MCrW&+2Zbsv?!EVLVB6$+`I4p zBrRyWx|xo;zXB;CMHA55l%g1w+-&#hs}V?jLKXVP$~ravD2Ia?yX6AW$Pr?Sw2C#( z%X6!j--?}a<03#8o5)x`xV0O8cUi2T>vymbuIkbmAtdIo(~6D~Cfn$!xMQLOot>%} z;*Eb=k*Zd*^mBR2m0XOX_Em5dl!A}<8LdIwZc5~Md6PjViu-}-(cOgN+Q5`|2V`B+ zzE~f8UQb-BtJqE(V*QW}5-TduI5x!g3XTtl*`Ax!BOnW4Xn!*XdtUvcL|gXpB=l=m zdrX!X*;ZfmZ5LNc2|pZCnB{n`4?hyv%XoUC#0ZXay3j13vGfYc4a=9is9&fb>|`e4 zE*1B)JdZ?wAl*lQynA%~gA-lHZDXY{oYWIB0=l-#2U&hS%|$Vn^MV5Gsm1~j1y61` znf8n;{g>;dPdQidQ5BE16(MxSzz(63_1=Y_%}sWcPFk36U14wJUwS$K_Z}cV^p-9a z56@K2IGXRT=k={0$ZCJ9y0P}q5d2byR-&!Oh;DY{e~5NOA=6<`ZdIksk+-i>nhC+)-Mrfw4`6po^s`MWe0M|I5-cC;XsEx;R1U;dHXnVjm1h+(v8m59ZO^G8$F`4OP@t(Xa$>RCvBdP z-ODJ{^JU85Bh;+Var8ySWnmD{za{x|*VQQX$=v1_@qCsW)h8L1{zoWW4xJAR&%5e2 zX5tVfz!LryUQ{hOc4ZZww&M^vRuXUUX)#ftK;>|}gwxvEdOn^biREy#hyiGE!oYQY zwq!pxFqD^@OUlo$#fU}W>)_DA`G2J^x1(=;b%7rF-*|@Kg6_d=$zT*%Q60i`@hY{% zX}f)Y-jGGm(;TLw)49}Yu>0^H4vEcub#EdoKoc(wGMG!KT>@p9aJ@n%bxw*sWoAUC z0MR&{oc;#oTf-&nHI`b{1d4E&1@A9b0UL`-8 zADuC|m%&hwwyEaZ`=*xLqjnZoTpNFtrW}aNE?NB^Z_Yt_X%c@jfxQvU8p{l!0)a-g zf9=q|ldy~&@#>xAdr{5gkv zEf2XZ?kpfbBW?s;8T%Rsl|V>u29_Ov!yK^>mQLl`YYY=8iX{vV;Pmd=z4T&Bt;V?5 zrYxCOZO6xE+T~AUYw;sK#4^;>Y}($Lrzp{GuN+H_Ngo`S499j4hBh)Nx?iFqfG*tf zaAh}eW*S=D7vy!BXz5W=$oz+grMUZZh5TS`oSnr=)N9suc5;c#=1NP5_Ge3ic^voC zZ8v*O7OM;Je~^I|$SElY7c9EnjIUW*T%T6xbryi^CIDSnv2phkdi`2Cw$8_|q*)ib-CD?k4lC zj2CWK+ryh+XA!zF)$6f47egrro>`Hh9fla1gXDUP+oM(&&#GMMn1W=&vYT=c<ne7QF-Jr4e0RzGRP>Y4OT(HLcPsXB?xia(Hx=Ae&Uu> z_%DfR!05l#r|)}j^2ikS9-M57-$gpF$(W5En*8s3vCt?$*`+b32xNFTv4{v|AtBgh zj6Z2!se0h`xb*bCM84)&48M6^DWcca1`dxYZ!YQDt{)A9U31+Y^J&~FRD}=w(_}9r zd2%F?Bly8&<`LZ^i*^~~`P#yvR83m#n|NQOXRz&$fm*%bgW7yRIs;b=421Bfo~&r% zj16Q&Q56>HQCMlttx!cfS@#hNU-XT?$!LM9ZxY#SC?>;F*bQn>aqLrFGIIeD zrmA<}S3<4c;VcXUtcO?xK&XC~?qzAMe397icvTrEtIbEW=@ks?&rO!!U92#tO^D^$ zHmSRSaj?KnX-c}h6Z=r;zn1|_`lUyrQOdnfl-nan$QhecN-;G0p?LY9W*1TS)+ zP9n#>^RnK;H<63Q-x_tdtQ(A<`A*A7OeB(%^3?G_{Ay&|tIJ#&W}i8qAIAPY&G?DTimAYleyl4f_^}jh1$S~~$Z_W1UWEA3@$kF0rdSvyNs~SDz zxuDo^v5~^wzxFp_dpH#YsMuAO-cepUVArgb7QPsop}~f_&bYEU@Mxx#8rRjT<8S); z;J6jl(A_@k&O+hX_rHtfMrm$ptDKiH9e_#4%H1A)rfkkeAqjV+(x{F?T2he7MrzyX z`;j9+aFS+0?TEidDz~=*@p5rNqlx4)V58W#BjfiW8m36%HAUotyZ}pP4T!sICq`v0>dB8qCIW)*jXC5g=JA0@KDA`W%1O zVJrd~bu4Qf9(}Dr!QVx72wL7@+ch6tk8^Q(_~;asc1gz3$~IZp%WRIOIiBw>*rnzx z3Q*oM<{T`=`s+);hBwYA^;R=#zR2RR8U+CC`|?T<#ZqUe#$r2{K85%4KKaRNAY!0@ z?P{sJ0^4AgLUU@-`dy61jSVaB+rLPrRpBvZ8!()%2!Opl>27z?vg_V_T9GJiQVeFg z+T0BOAL?69`_|jf-m4=x^VWrdWlYcTrr~mynp^ZHJDN=%=u>U;zicI$XzVQx)`H-h zs}9{V-f2!Ii02NYESQ3ARzHgtZL!GyDo8PZWYsGXyMJXNa$tsq|GO! zkM5Cn>*XNvp*>hr*DIToWT*1BO5~c#jrCYZq?t(>5(Y1HX+Jmv}2KlW`@@=l_#E=b+*#l1&p}F#dD9`tlqA)FdB;{Si8V5m01ghqcbDn@>vV zznUF;GCEm)5FB{^uNd2P}HksO?V%uVl)LA0w#zy{_cKXv(EYTZtoK~1~0f5o|j@DS!(>4j69sW58Vp` zw#%;NwS|D6_LMBvX=7^E89P|$Yst=10;Xusr7qRbP{p%-+XzDyCWEYB?u}?TSShxT z-PsB{He7Riq`+^=Y0>7nJ_ipKF1DG@h3RzEHl5vvP?NgkcuCu9EvSh%8{fI{meI0M zOqW`=cE^?QabDselo;POC8tI~1{AKT6&i{ZrwFooZ@`z8KB|H(1S|WhA8JMvvx1&I znY`LEM@@`c>Z^9b11Rslw0>-}jQO5#8|)9CH1Ri|Z}t((ey2~~y*likb$~w^!ZgpX zk*9rtDSBdUI^T9kv5gTEXOVL$zu)jwf7fS_UE)ndW8Oz%dDJA1?2WwFW*RVj3b+ZO z|A6t-hN#2$aM8FB>S>-!hA3}O6~ZXx@UCU0O&j-Z)g^Uqq(Hjfjh%Db`U;C;wvY}- z)BIL%LXWEJ4c7o+6K7{$eA2e=oV3-B9G%{^&Q9lSUAEDH7Qfp&>&?sXw_F^%()f?M z?@I9}V%erGtxd^mL;ttfp-4ZP${)Npm8XAux<21vZ(Q&U$BMeF1DF$GI60TC_3aC_ znmCr0m!UKO@MbDUWHU*bJnJ4j=?lU zg@sdIuZl3qkN(@!wfGgymZOe%ozhUbb*oMq!{SpAsK(G|x`V#Pd^*~XBaC97XM)JD zDXI41Pam80on_uEFwMJ8kp=pxa542q-Q=k_&G)o(*nS_#M6sbK6{0}tqQ(o%Qr znm4xfe{mXro>1bqchFzFJh#|LdrI?WdGTAehcW=?Zm8Z1yN!TVlb6Ahf1Eb%mn;FY zbC>7g@pco0-w}N`$Cj5XQ|>)7-(Pt=xu{TH2tYV(y5Bd4{%G^L<`Pivv*D|X3`Dn$ z{d;;G?Qi)-h$p-uDNwbd02lj7j+IS`AgFuDn1v@z&Df07U zAdc=R{~i z*>G-HZ8)HM=v3?XAysuvWQjp3j&+?z@tw_ZK~ZsW z_^O47k)>u=1x7Ir(@FAIQ^nxYLv+2BOfnZh(|f|SZn?q)QUNh2Bp2W}BjvhXKU$mj z%IbT{{D$<$(}i4a*8`y3U@%4B)WZk9Oo#elkeqwiF)Jb5D zC4YPpvMDZ#Z%vp-p_Pn}?*AlZ+U#5?fNgL$>6yvF z;R0j+M2@gUx_OR_P=)plW3~3}pGM9oUoq0^IBQ^1l&*QdAeSpNa+bsX2`UI3}+O zj~Z)w9L)z3ELZgoPdi-nJ>Zd6`dtMp3^vadCUqzocon~v9XrMpEWK(7Oz$ZwsTuyJ zkG$DgQ$#%CCpf)~p1GnZueId+6D+an*0T?lQ~0Rvx?f->YFPRF{Pb%9Bo&ugs~ZqS zAV#f|L#fV5e}8O~hdXjMgUy+h9uG`5^hK*1qI6u%*AIfNkn!M{aCm>X2yhA@5yI)i`UZx{>dAzK_ zC|2wF;FO)CCexvvy|pDzN~6M=ev~`j7t%lxD5_3sXmj3+rbEwzW18R>87{jXG(p+>%B`CgCfQ;on;e%kD-0$e56?;;ZAULja zeO~PsMj;F~y>P4mx4k*JX|pDW$HbZw3#h^XWk#Lm3`4M%;J0@=;|!7m^)atXtM^;|Yl`?(kKDX;zKwQYmu%4r^>y$< z{tJn$LS8%lu*oS~@A5IJ?y%<}t+Zp<#4FB#|A>3ez<`8~{c{41nz-aB_`ZccTVj`* zo#uVP0FHP=l8?JyJz1!v@V*w}@%OQuCFwZGN~gI;WNt~;~IP_k1t*Dt^ACP9H(J3Ue=4(_@BpaA^~Lh zDV9lxG29z4+PMJcq3yFJd>#jJZDkLi%WibppQFj#4Y{{9(Eo?aWzkzosm$tD4D(fQ zk+U85#4J(hYt*5ZTfoG`^uN2mn0^R;XKOWDMGFXpX}g;7;}3XR?v{~+G{sI}ihUU`Bwvk9i}4X@_8P6tM7gcJ=p}TxivcC28a4R~ zWuzv{B8C!&?!qvP@d3xj4RAw`5AHCqOf$#9@YmQxm!zeB2@24} z<)al&mxfzbr7GtZSM47t#kY#NcZ&?5z4uJ>2_Ys5*4m$l3rRcbSoM4=WVW zyA8SY_C1+*)paT3yF|WnT?unTiJ?o+bG>7iEoXwxbZ2eRpK!u988+K#%h&=>#R6Qk zoK7}9&XSECf48*bS1noU2pTFbxvSM{%uH6A8$e3TO-@HqM1cE}N-&m3ZL-GDIy_kS zq%=wK{i6^UMIcerz9K3f|f_> zSoE{rjjP0ADTD8Ee^e#95rbUMy(jOx>s819eZr!PPW+WpU1|X*#${~2Hrb4!{p{6n zc~3w8{jUb@6Yx;{A0pe`RG|`G!xvcx2Zu0S;3cs$!bSag4k+6R41JlDpoLP&zO^=|aCeP35h`V@RlTYfj7s&*88IJZBDTjQyA0pj(Yeu_F z<`{<$&s;X46{2buQVSWV+WWj87!+~9!!*DxKR65A^3d`9f)7T2A4$b&7y5b>$kI_*pQXCE!1q{XA z&xaM1pe%m4N;MsvJfyuA(>C%|U>6G)$_+|vz5gWQ$ft$W`z zuU00@QQ}piQpA6UX*4i9j7UugopwN+WS`VG!ko!~AcRn*0K|H^CoCUJ*+`v1!UWbe!Xk($BS`mXKeL9pR>D10^Q%a7VEz=LGhjx@CN zYI+Q`3aE9dfSOXND!J4R^YgBtkG+H}_))I+_{(mh#@*YP zEbc%scoBN>yzTa)dr-b}w19nk%bKsznn()~Dn3D-E?s}$=9IP(%;~gW@S|)OqUr_ElbmFgl>p-jV_9b(tLvXKB7Du5y$!rcUq4!z}RbsmOpXMgF z&+b-#CLN_r4chfQt~*`$=E_?^~6sDPHRtBc;Yfi&onaER}To9 z0P0CjYLV^VGR-C)H$~QF>y|vTj7U)FRJs?U;c)u1!r0HQN}u=*ozDzrTtWnk$~6+) z)z)(;f+Joyg00Ko1dT^PiUj{t<;>TYMq^6rs!rR0(zKg@m8E~YWkGPO{Ddp8^}CQV zuiU?x>G+m*o~S6dx}tDcDU|k_`3qOISjs-*zvUu;;(mkHifiEJQ1hy6maRIs=W{Sd zAmwR?(qgR74SjBVp2s_Js{@Y&oO`_l5Lt(Q$asG?r4hc-(7PQvOI6!a^U~+dA!4h($CC z?HS8yKaX;j+NM{~o#k=JNE^MgQx}7;03_(C)EFjU1gABR`H8`q)|$WKiUKN{6SkwY zQQ7>(i{m67)m!675XC-O&9*dD8Q1G$u*bswmykxyNjx&QsMdrjF0}FT1XYvU@m*Y4 zA1OCGB1U3u6zinH!UEafmA635&p><=m&MAOX>6?08Jv3=Pa|#o)orxn6OxaN}KGfTA!|0{GCl zCBl)QIlJ4gjOUd$uJBGs+vC|M%ntaRs8FKP5)0Ek<(+pHVD5ZaMBM((Oidt_kOHJY zqDhImAN~yr&*aO!4+nO*^2yxA*rCv2ijb~ra#4oSB?)oJO?j*z&>>D`#&9bpQXG90 z`6{b(+k)$xPi{#0T3$op&#dXrPG}?@4EqTUGLvW5y8_OhZdNx->Q;Q)+5~wV{-qOj zw@E)-9Z(5U#iAObmWeKhPky9_(cmRoY1-pd_U#3o1)}YRCHyT4+-J} z^n>NTH$Nfpce%}F?;RwH&zA0fY6|Izlvm|)zL<=;$(odAa9qTV=pE1=?9Uy*`HTw{>4CvBnEP z_M9kCt;t=CJyuDI04cV7Ez}zG7~z)0Z8=%jVlw_9q2V&E5gf-t$#6Xz$5=Q{Q|(+z z1*H7%W8z|FZakBdlQv8gh&Txe`Ntc-1BmRtl@tO?9$os?1Q`HpDVMlk7TXvqdyd$g zgj4?h27FV<7XxF%?@31eu}X;xe*yxA(jI-i*kx+|CzSO6YM7cen_c8ea|j6tN``3! zHimwl^~eJ`Qb}_D$m7quAT8Nj8H8`W(%@Te)8_Aj!;gG#w3Vu5p&v-wDNTylCb{7w zqdlrw3#KR2m;zrRn}^v1fGU^yIQ804<%Iz}+`95|=3$_xNdV0PbteoQTvtzzNP>O} zFxuW|xmgi@H7NJR=UfF5@k+%r3mtcJD3xjR52y0u)tJr5@2KXLA4o|^bOITS?es79 zxiTq1zz!ff1=l9~T_msPJ7eI^>EFDQYxQuwTb%+76^c8mvM^wvh!uUVyBkU31Z?-{ zCvznloXyvZ=63?eA1K0hbET7F3OZSxy(A=ghmP1kM6MnvRezLI(1e*y=66?4FejMc zNOeEvM!*5V93LMKY94IwHuAWhlLLQ{60Rto?41^X=vKG7VV}tDah9G%>oWx|yW69> z)h$a@wfgf-AUD~QFPAmm>iOW|+2*i4h!KLvi3FUdA3N9(Dcz|&**! zkr7 zNvwB1fCW`$8?zlt(=jsQkOvWD zdu!%pX{SY+@UwkgjAi5Eii=KVXz9M8xIQ0+kSX|2muRRw%Zog|%jW_jSRa8lo4R5x z@j(>BIwXZ3{V7p=lS-N7bn^$kK?5}{sdmcay7F6_(gWnQ*+Hq-mxLA-gHG9?FYe>n z$r&OZNB`T8Kt8A6*(Q{j=Uzd+ngHRBq;eI~o_x~!FuK2nE7ZISu!2u}@vQq7ga;R$ zKN+NQg>M8%2?rqc00EeD0!=bVEs1Y8SQ!tMk32Wn*QCkE#|P8TJSb|O3KfB>avCzs zpINQt1>oWUj5Y0@p*u>17K2UBXM&^UWA^xWzE^oOe{!6Y9` zn*_JJVwC?U87G@@s$N>WGb}@n@HqI&yNzI`EhdZCfLobv`>(fy>c2!oaR)khc4Aox zy4?er60tS)-zznvoHp?EQzF^Upl=&}KGN3~BT#TI53+Hp% z<5PR$>pN#5<*BzFFbllB1T^#JN$$= z43pq-QCD3dhb(bSI?6kk$)Z^kaj|F#MHYLHcNcavRU6Gl)8M@V;2N*^+F_I1{d5ZO zdZ2Cgdar9r_mm4nRhrKR!*31s*f^v^MY8!Ux+c$b<4?cI8q&}*o-{Y)NCE-^Hcn2;+TZ^eeKDxQetQDy-|=xE zeV0L}hmzDE{TdW@XhYxDu{*H9dg&dqUMQ%PfXRL>5RWyPM$^g8S~Kyd>sGzYqYp{- z2=uf_qYTTWvD0O)t4VEB5^hGQTKa$mB3v>MRv4 z7(vG~>%MD!JeP#|@L_#QRuDSGQ)9J^yFC=A5?j3j+_TKy&mO@Y?11~!WVRS*#r=f; z5m-ZRM}vR%oEcW{=a)h3o2WX+)b^+sJxrz8?4TA?Yq5|yTp?9m2e_=(3i48;0)SkO zh6TD=X7W@rx4lJF28DnCA>e9N&XvHDQ`W@Cvh@r)P~?P|7XZxm20}VpL;w>ClmvA4 z|5b9pA(xcFBqOY<%=kX`TNI|@pRewgE$U+?qW^?}85}pUC{pPFIF|Br7>%>swjI7% zD(`P~tgz0bPEotvI)U&p?SHPDP-LGlh z*zAVVA(^8zQ>ZinjBT@yFm_kuoq%jH758STo9h*^Zv{R90W>Exkb~TR3F1WjuDRQe zPNlS)bJc?kne?j^%nqcz45%upC6nraiAMvynr@&wq>)Xr#qDa#+yDqwLc+pC6%}JV zmLbG(8~y$i#~PM@YiD%AH|ik=3YvM1LYk7VYs6w1wSr<9G-%6)_ioQNxjb&mLH{c= zYoO6sW>HyLNm4lj`58wqUCS{piWD;Op~oa|+jT?kVYjFST&_ysiNqUZwv18pY9+>3CjGQ0k{t(6Rg zixcfEmu`U_{rMc^hw7%7U@Ua_h`M-%h%8(Tl4am05p6muytz7Bz#xcb5y# zM%`~>n|j@+=dKdIn>+4Zp4`7qENCP|Nd9SjvWvO)=l{X$Zaz%d((|-2pmq>QCqleY zmRIN&MCx|POxF!*c<(*6k*0yL+BdH}$BQ1b%(V{D@*n87H)biO*3*W-WJAN2GV!{$-}Y>#NRs`>_sj}l<|=xzl-r#Td!)na!{du(F0E1tA1K( zut(b&O zU@qsXEr%+kqt|9Ul1Kr^vhCOs$pYCr26y+P8~(qVLKPB>s&~iOyk8=9+^Ea21hCbp!dQa4QBUBff<%L|zeZ`p0(Uu;lBrI7nN?ESB(Ql?TL zgr+j-(;gmE?d^}jBrclFoi|k2zOL~Due&Xd(Ratv`cwckZj5WEVv%ymR@fO_2LQac z*&QV?9E|_>8*4%QTo?v}ix=nycAkK4?-sbgpfviVQ++_vev3G%8Nf31`+VN<{Rp&$ zzh65(1;xZbDPcTLt7>c;iqSH7c^r1L7^R&4ZV(XA4@gT(15xEw8ISGY8pIK0eu?fs zCm#f1aN4%C-0v6J%_jB{x=R7QK_vIU^UKq#P7Zv<=rk60WwI%#n^8_q zIrwmpGK(e|isF%zz0-Gi%Q{KytKVJE!y4Kg;?yrsxZO@rQLGy7qFt8^(lHo9EgKWZ z^5NG*NpN7hKN=jZ)f8=Ed%blS8;O&4MuFt$`~u4$de2W;c!0I)1B1BsitOGIAqIiu z2}xszg}zd8c9*62+`&P&M^J^(hywZ-6qA!4@XGX8z_&ATvETV1D5eS6o~OS#8!xAG z)nw;eyDW@w7bXZGQPJZropnXF>euxmK0r{04$kP6wX&vzaDoSL8HaIp;gF{e=k31s zNgeSlH?tVnQIm4elr4Pmp{VLJt5ZP_Kc(?$fa@m*AgK~ulRPj#N71%havXn$UHBwX zvz7%4&)=*wZ;jjbMYs;SOJ6JP+>wgcjp)T^A1l`T4ee&DsGROv1SAzT>t#GhsFJ=c;A)ZY8?y_l)Z{jY!Ye zz2IjF+WLPW3^aNB0jDQl5nYWAY#I-Etp^rB^r$JLE$%n5yro1A?B=uJ?LjD%<}XSD z3-}sElHi84NDts)#1u61KO9zJ`q?58@|je?U_c2;ry}~f3Id{pn#`i%#B}qE#MR{11G{`aS~s8hEBT1+K!!PEC1PF&`asS0O3irviexZt za~`rQ{dM3xX>+2DJ2}ZFEU30FRQyXXG+`#}p8Kc(bJG=0g9c2G4U4lY-08MSqs)q?~t?EH^{ z*wTS2=J3$&!MDVA(2Ur(w`TV?w`?4b@1_?<(4Q!QmEgw26Y?~qn_lf&Tl1@v{Y${BKhGQFzxA-Z=gU*SoW!Fg`Xt8 z{6|tsuf8gEtQ66^lg2`wGG!Y-R*s92%?8fVXdKRo() zL)-WiU0Y@OhVtJE8P6xV4?*RDV#9>S2rouRy@g?ZRY{!wvPt;{*b?Adrzsr`%*SNB zV4*D`YZy<$H`dE%WI!QIn+4vzJKOB5_}TG}d#DRgk+x*?CBbm5@TCLRl#mB#|2r}X z@r5YWU0`EOkrWPa-6U!*QVDiQlfw7QCnN#=IvGjFMjL*9%xg}&=Yss57%URLYWf>Y zxs^g7^!b_-EEm(-DIf-ajb~qo4GO9^#-PM52U@LsKn4LBO2On|aJ+fF*#3E4ZEAR( z8+Uw{d{Njz0iX)=G=iB0OW7F5Gp_i~)_J5X6nLCbthQt{=$z5)^$FQRnU^jHscek7 zIlERl;uqc3pp)4adQ%>S)VO%k(BR#i^)NOsjYMZUc1hgI?YY3SzmvBq)$~Wiy$U|= z2~Pb|V_#jJk*D37|1@hg9?=~EQ=gn*N~GYvaFO{5hxArKU$Jc_b~m4+#b2viYsj)N z9^=TzjbG8p7*BCH(FBarqRGKOg{!g8+E0=FO{xmx3pawFB^;y_s|&ZrH&Z6mp1pWJ zMm7x_98Gi+nwUiUC+fP39{G*vm4cw$tF@(Q#sq zRW=%pVW~-J%>a^8)(PFvgappY<6ZN))E=u_xi0SrB5RiAjccIzt^SIeL&V=X34})r zCK);kx**)YZYz})aJw?bgPT``sHU5N5bCx=eEf)~tsnrIzR*2RTLw z+(~&*BuTh=wk;xPvRb&2jd90NTsYT7Y6zUmrW0aLRJAo{m}^Mm$Ht@;^Jnl|EXV-Z zx)nZd(8Z{c7?%(jpy8G^74Q3^ykQtiNgL!yEXyTksCt)YHBkJiyHC762;cmCi^?Am6B~YZky+fEeUY^LQ%HxZz zW`O1XW^oNB*$=x^?Lqd^QqrZzHC5wo2U5aE;Y%D^(rqOGOY`r5rfP_UyER2VHBtbQ zn)vEeH=ZR!i>ox}w94pagZ-a}oJAS)_|+2Z=Y`WaGE<`MW^1r zbSF%?q=eu7^$os3M%83B9Q;yWzjc;yDWFiK%+N3#N?|@*+yNAEmYDgZpikSFQgRXU zoIq8m(i;-^#wQ6N<|-)S1`@|z>@qm#x&e!t_Y;c&USp|{hOO-)Lh%QZcTMMbD9Z9+ z;^=xzBm$n_Kr4cg=Kahq8ipVE_C0L>TN%JGXb9NkxJp9)X1sHA-}@aG}v)3SkrU?NrS7(7CqOhDcJp75mScn1v%}-PAC;X?m%Ngcqe!1c-g>R z_lJw*q_otL6rwPT^)(iLVQHh^>J}tVmx@JPu8VZ4!_SFBy)9ilj`3!g-77Bh33vkN z{ma-G1pVouL^Yd|ApzWy<05W{Am;?W6=}CaoW^%!(#c45nA+vecC@Su=D*RF<;9e~ zM2Vv}dp+Ij;dHz2ec`BKT7a0^jNJJ2B#vxW9?vCP&8!s?RJ8+0ZkjAgMjeH3R5E{b zf0vad7mgn?OVhAA>{QAPP}GfTRVQd4gGqa{r4*-U;kn=RkDPpPDXPj&p7`M5lI|q{ zGB~KKiR3+(>DD2#8f=V?0>^~oI4(mGKs)sriFfDcA0ooC?JFRQXkk6}#(Fzc`phmi zI=5|Xtj0g&TV2{qN;J9C0(y^nx}F)h|Mr%eizu0 z2CQ)k{28C0PyN~%#Pc{7YOV~(|B})MbA&8OoPVjF4(lhE&k4Hou3l*D##o4@UN9=K zNa`kI1`!!Cm!T5JVyhaBwT1uo-mPm@mS&n5jw3N$1SGs zfsm$@(qRU#97Epz+Nd(E-pn)(-Jk6e)hdP)dmLs2D=%lnGL&Xj{aby5OkV7R{bc^e zeJ6^kHHY6UU|Lg|G0QHm#5iAG*7GUx7P$NpvhMYl`s~XEC{IEP0LO+D78VI%(X2Im z9cIbeb+%n-+9x_h-%{(+%atJ`>qhF2Pu`g(?&AuE|$AnbeyHp@2| z62@ZHig!RQ%oHos97k;C*LK#6cU}fp08`*+uIU-fUk?lEGu#fyAhi0>7GbT)O2ngE zlh}F+WrB+a$t6;QEwRXs|7~QA*kw7NRmxZ93oKn!?A=?I#HxIi+lwrBqeNF@LW25zbhP{jf1x1z>5`zVKpYBX8g$^vmU|!7Mkp&hCI|*A?ZH6&ckqPI;|E0)?`5M zUQfZl>_HV@PYGNF1yUGGUC0{GP!X$>G^{gz8B>x3ZA-fGV`e+qtk%H2Nyc$U!2(OC!zhjs2U z*EiY-=kK#&mn#)ak4v%rEOztkuZAwR@jx9r29!MTk&a3+rh|jSzbMI;?}hGfnx88o z{9Ilt^TF>efGi^23$CmOx5`p?9B5+*`7DPLN<6ie_yIkbyIwTIL%Ss@s+eFvr16hb z+-M_w;I$8YcnH`a)vvm2QH5G_ZAzP0OpB7%w5qz;Wo*am)LoNTc1#FMIqEn0=2(2g zr1il_Qu&X|0>$sDc#w`MQWUDAEJ<|hOLucp-!_fVnV~LJlCW)}+`dYeFVD#UCJmLJ z3movEVcqfvSn8btlvH+@NpK19df1#+$@n$~h!eUmfJz837M^dcp#MQb`N$^LxIvN$@fac{xcyG@TkX6cRHMBft^BG z4(ML31WLNiwGKbKU!nERd(W+b?(y|;LB)z2u99jU5?M8<{twT5M15fBaKRSRXo<=+dP{-tBq-{sFr$F;AF41@|=bRdhBJn&(GUeL~?+nA0XahqAV zupiLCdE(LzxXd`8l`Y$gIW_vnRPuNIo|zm)tL~xOroiWf9SN-Zn2^IwUR&F;cv>rV zqu(N4X``g?1RR{_D0$U7_A zRdk@O*5Z{f)+;}c^E;348~^d!8!O~jYwM}i#;3VN#>HXw@84aM>oRqN?SUr_>>!9n zaJjMSwz29;3NhH5%(=aq(_wKr1-AMLY2#K~ETDCCbeN2%`+%sdNy$cD2QI+$b`Zz1 zn++8M`RsEW(7lr~Gnao|&Ebad+*n!VR1RaH;y~WuqVn0VcmCn?6BY4Ks^>cMTyKyk zvT&xMMQfv=-7_aAXyR1pg+P9-3!4jJ8EMtHIl_2ODy7L8PhIY~$R{m|QTkeRdXF?2 z>)vdvR-DDIx9UrC?h{>f*$CYaf0*vUBMD}?8=*e7ZbZCr5 zl+asKI5Sz-2S@ToLKZ2;DlJsLkZu#zxpXvHew=3EK@U*9d*UC@i2n2CUMy@q9;BXZ zVK@5PG7P3Gm#FJ*_J#sFDF-irFw|xoc>pYZSvqZA=i&Gg_p2#6N&xHo)e}C@M%Zw;pB(yc*1t;gvrS{n*2I@y0&re@2p)l(Y<+U9~;B-o5zvn08y8{O4FFRvl zpfp4gi<{&ghzh`#UP0B`8gn{3x3k5R_%0L;$zRYN-^m#mX!2v)LP^TU23v+lNs2T$ z0QB?)?+drivN{efYSSkK80$Jv_|T@;sWpvg6v~+rbLQ}ihU&_0`5)BtGn>6b34jpqN4tGAAes_Xv0QA7{~rBwtJq`QS7lvGN(OHyL!Mp~pnS~{dV zhc2a4Kyv8rW@vc!c-`0iy?@X7LthMW&OT?Ky;pqJdod1M8kAKG@gj?EGSCy(Yw+Ve zJkD2$k9Xiay58JnWo6$vO(UnvBt*s50DI7un^~%rYmtwP-4FvQ(ncQ%Adhqh8xXU}7S}$Zev~w+;~wn-LOZZ$aJrvO*liWi&=Uu4j&zS3iio+)-8Cd9*0{ zO$LzcBM%jS&4eJSRjm_*Pp;12GY0?V!r=#jxJs$H6Tjg3oJt`dbR#%7N#Vmz`vu3> zhY&$`jkFl|+1A0yNxF$LXVkhTPn70V$#y39!nZ`S*G^9rvn^U`vS3(@^q4TdY$O6| zO-DV<3O3gf4_Cj?qmnTuy1CxtYf8B!<_n+w+W4N4u*a+OOn=Q+ zt3RFFk(<&9rMdomw^$ux>b=s`TPB_*WW!%`hwX`>?H3+rqS54tu&}VlsPbj3_*zTm z)NE4nfx0}nzV+unt&-lee$fyH6tIy^jG1-j=Cp)R+>h} zI4SEq!;cO%WWRjehI8P0aNfD7%A|Q|d8vP|SpehhmhHKiIGov8{qWU;`ub zzUdy5kWrZCp8S(pQGj8z-DJHt5H37TItrT$l-RC|$M{}^vu6#E(7#@qe2@+i{jG_Q z3lqS3Ul(UO9AVA*oJ03f%cA0NrG4?;&a*CWA#T;?CWTe{`wLi)D29&b%CH6I>CpA4 zUVKJKi>4@~#?gKWK)fny6}C*IF#hp(j}m>Ar(8gUG}3_rTsf3^uSvbLomFC^;%-#nA@lxPoE0Wkwc z^oa*Z`jf8i6LPd&@nx>wr!O(hjymk#$#v|=p*7a4souX)TNb=m-0aI~G?lbI=d|em zqn1g&+`>lMK*gd3(u?=qd`>7MyHL9l!Kdn@I_*x zNjSxeWxR*AIU%5+FZ~FIR*{NwV*vYVj`$K(BjlA=uv6Fxi~aB-@s*N?KX&c29=Q7I zycFN#GUt>;p67poGv$z-H0QJTqcT2A)Hty~we5mF1Cs#dEq_>f-?Y`%qBC}?yZx;9rJW}T zpZM|q%{B3Xu-guY@%v8ut~Cy}%-S>i!~kC@(8pGyYR6q%uWuWpE&j$U`{41HzNa`J zB3kG{TZEkZEps0x$1FXb@H5}Tr5I^QL}(GJ$EJT}`KdeETvO$}4wsqyYFg*@!Yagh zT$QBFmicb$k3VZv7z@PikB`siwZcq>%KQH=aTM&2ThZMwjw3jx0z#gS8CFeJm^=ao zWV-ZIr9w5_(E`mIcFlWAzkRcS(mz+Lwj3}mfH0}XaVLRY*;-#9HsEwDMwyO5R6Iz# zzHeN$Si2tf{4vd4K=81rcRMW>L=6oN4*u-EcG6gjQ4YFugb8A-A$H&{u|R8z1Vb6+ zX5ak}8S1$jx15mZ7ye)?als(O?tryIY_C+u$uCfKE8_Z10?s9#(=&F0KK*{kc*)wj z(94G0Vce<{l3&g=IgDo>ORfy^-I~u8<;7{wsv#GJuLcO|U+(`Xz8JTusBP)l!_95+~EZ0kW8%EF3Z2D8MM`_-EbN)dtu z9%S1kx*kIl7tTs2k;l;_7}~LJ@Bb`GSawY+{MowLxt0i#d^W@9gw~wGb5&Q77D+u6YE*`QA}PLh>i$I1gC}Fl7*^nu@jQt>y(u1r zMCMRk`j+=jouT|Ap$l5UEfwYXo@COgjJ4o!dd2EeK3bGkDW`?uyk1*l638DP5-DL| z%{?&i!X|`-r>6&~JwtZP3DIA|+1s79juNDV@Q^?B9FgY40dQ|yIwI+Pe^n9HjnsRX z#7@o*k6QW&L8l|N_&BU^L5?y`MvPw&x1asWgu^?Fn1hIGR!0Qhm%&{cXsA(2GGvRR z-1*&-i;79fqTfQruQI(u>odXK`>O7vJSH`DEiVvBR`aI`8@OIcKjKglYpl8A;yG#_u0BEPyC z9TDLTLfV=hO8DNq6lp&rYieE_g^)!%QnOR3W0vAh5`&`b0%^jKb+xeYYBeqO-2g}1 za;U$qe7*e!-as2U}E+JDyt5jU0_?67#xA|joEDXgq5f5@+ z6zgydyg;l+kvZq59B}{1-}oA)J*Bt1CUBrnxpnsr!YZWG6rlaW_V;Gr#g~VX($D`I zyxP~05N|~=X zKQutf)EAu)tYf1_{)TFooKm84SXQn3vPiCB=Lh6+{4wt>egoeoMC);U`u1kVEamp# zD^RrFjzU6@JCOh6%i{77%@ZkZF$#UeW2j5!^Q47YBSzz!%4fI{5dj1uXLtLq(#RX4 zEg*tX(dSXN_Y2owkDs=Fdv8~2= zJISDd>NJ(wwf|vC1g=`6A&$@7v42kaHSg>CUnZZ@4g{?*J2(I7Lo}{+Q)Jm+_SjoS zWYsjTqJ{;>a-USFvvdZuD)QlJjHap5N;spm-ZcZga6jmXC&djlS;sxfencJQGR#^Q z)ll;`Cva+ol15T0{x|uW+(XmcJ`S2{2U<+~fg&{);IiYIdXie^RhF%{;k~_cPSrzi zWE7LkQVqCQ!GnUA4*}x7ft`j1_m5rzuV{3D>vDtyzzoTz3ic0pwEGSZkh!kE{8M7Z z+q??W>R6-HczAfrjz(c;H+kw6#yv`tFWD8;_--@!r9JL`6+Qp{GxpodjEwZA(-;&y#dM_R_xEMJo@NSQfx6Ydw~!HQ6cc=wbSLsrnB|>b%)ub<$uSGMJ%D zxnu=+a2T`GD?2xk*5!wpa$6b4<)lBK|` zTN;N;5vBOs>MOyyW9}Dk79P3mhi$eM+$4A54fR4)GeU>Ww=C#Pt*5DopOga~Yb+4R zGyJa&{w`Z#v`Fz$5c$6>eUQl{RgLAV!;G{Mu}>ov(BPALx-9-2)zj0H_s!4eWxA`9 zf?Uw4Kl&&wz};K+CQEXpMjU{6q$+@OS|mQsiUuu;c`4)y-$zj+?`ipC!dERlNuY?4 zfR+Rg7htuj3(y?iS#S@R(Ob}HG?wFC1E+L`4~ZX0XKK7{#;Q4PW18GFKZcw3G}qe*6Vli%HQ#2AGxI(T~_BfZ_KZH+ab z3QSy?kBaQYpPsIj8<=1}O%WB(qDjfklV15#Saxw_PQlcr`r*TE) z4wH;k+7b0^h>^M9?wJR?q3Y6uQeG-si)dN9Pp^BgbyfffBn@KCjp}Xjfcmf)xVrY=xI<}*-V2=1rRG` zh4OW=3!T2folZzeVGa*&rb1)#RUK#~Z2H_tZToe|c;S!dIBvNfi;k-SR|?FTcf2`~gAXI)ev&By>d&-XM>qGd!XIx5gmuxUTGG)% zvuT~ZSvs}zstnlwpBeMcMT^_UByn3hD%&>Z{&F(Z^l`NyE*5P(By#PXnmf#~X-iup-7Eo<>j?X};J-R;Hqs zZJP+`j-VgcR)6}+ri0c@oKP2;rfqlWt*dX;Mg+K$u-wAw0ms2d9ftOit)S_qBb z%N}fXJaY2z>&sIH#W98=FsE;oN_4bAJqtZOy>2T%MZIu1nZR-yv7IOc0}Cq()wr%B=a*rDK@5%>I-*Uq2W8`~>JHRkIoKT=9J8Dh$)uWk6G_uj>~0RQ0^AmyfELHDh8ca3DY7Fw@`tzJ)s?WkK9Ov8jSoksiMvhv{%gHoJ0I62$Rse{+9-!Py&=FwXw0h zcB>DmrN6&GFZu5HHLwpC{SjGPjm=Ui=zd{j*^LXQyi1|uw!`)Ld?gOFpU_H)7j6>_ zM0paS+=gdmWyK370uhr&%lNoze9!xPpsl$836QDViib_ZB=~8{N2QbblxNY)Sg3G0 zU+@Vt?UZ0(#tV6RguNgxOZL1v@YmYyP8yyX5as?dE$JJ0P0-4k{Z^I&Me<(HcdRCm z@x}%*+iZ~k;tK}T$0USIKm$jG-6^b{{~&3?BqF(80rF|0^yOC^QjPg?Ix3lac7f7z zs{saNcz8ZtUGFN!L!CWZeb9jVN<8dHL^Lb7W?b-U|JMa0_k#xyY+JIKojam2ig_J2 zqmw{a8PBM;Eoh#j?|tW`BI44UIPbH`%s=wn3Z;9c=Zp}LJ720c`08dWrTeI|yc$a0 zvg?W6*^V>d+$Z39dKRF)iuyHJ!y_!u?mAy?4OWlqgr9SC)~7N?c;ezx8TRrUFG>7s z+_+rnmCDP^bZp|xP{OLp5!Hj#eBPQdE3X**#sfnEsoKb%FgnE$j&Wxv&-+KSpE9Kh zOOB6-t$7(V%}}uq-nolhnqC}7yQs+}xYMkke!l?G zGvc>z-y$RFE4ET~3#W6QouI0WSxCryjFA@-#GA1^STQJA03OQC%xw=tNms8rg%2fS zd(s!kYZV2J7u7s&uTL+axJ&ve&X#QfxL`I!K70Z~6gtJ~+M?Eh7&r%~r*YMq4}sb? zGCU`%oII;4Ik1HQ_sSsfwG%~)n3JXG{>c8quN4@O{`TQQg)MFsKL}*?v3-c{z`Mh# z@2Ts4qUH`e`4L#@R&%{8z`^Qch0dAUn+FoGwWPgi!7@){V<&TNohY z{Y*0P-=a$t@QX+i22p58-)B;jpl5CCG~D4pEGNG#%3X1gPTt~wV5R!&h|Y<5ajm~P zCU5v9$;NBfo~yNl=}AO`T0Q9p0cGq>id4^A4-_QGEzOP2jKndwJZE{fLsDGD_N$;& z^t=+A35s0DiP{$n`r6@2v;&uUOpBUNuMre>{ih`{(`z+_nW7u7rQimt#3y8ImELe- z=K1XM_!zCEDoQYM+P(g7;sknDBQ{1lffYG);IP>CpjIn%od7?1&Wxff6QsDZHs^k- z7HdURqy!8L-=2Ej=8z*>s`4%ctxr^l-eV1T6VwnE>U;z0r=on(4k^OoyN1gRyYa4J z0XA%d$Ih_?LlKjp+6rLIKs2fg4(*4MsU9?#(tH^+~_&e0TeP8&WDHB);|#j2=JQfC%*AbuqQKyyrWPNVrUDg(rJ*l?`uy?7WIjJE{r{I^ zQl)P5ha%3ZExX{WtQ7!Vee_Lwo0MVE5BMrC9#m(C{w`1%uoWA7FY!}M#QKfoAt7U8 z1fG(l!J-@sr^0n3$AE$@dN6Uae709s#n{K~3y8RpLr@S?*ai|{a##3&0492o zUxMRGU3zsIZa1Fwg)mReryGZcqVnEok@=mNJY0!pP8A5Pt@vqY;a@Z;^%nLcES%*t zJsmy>Ziq@JJPwpGc?jKX3EOeLuD_?eFFC-i^6b0u>pI7)hfQJJhA2~nwWK!&H{O1t zidn#8JmabMyP#`%Gj%k$hIoWV-v4v+#0BKxmDApO|LSjY3)p*E^6P1MT=gT2h* zfqx`Lm|vH$J`@Fs23nCOIsJ8`Npk#67rg3RfP*#8D27JG=O^|;RVUs8lw9lu*lJMp z_FwNeju?asP*?M~UHV+R>NLpS8(&t*4-Jq+m#3GUwiC^=^`RZKZGOz>rn+(6P!DVItG`}C2dx85$`4HtTa8fbQU8dc=3GN9q*Trnn`6b@m;6+ zALq9VC5mFY`PsilEX6x{1Xgealb;mN#ggG8^jdX9(t^X727)!-R#RGM9&GJS*La81 z+!TAxY|ZJ4`y3|e6^BmxU{DV==2&)pAi;kcmZDsREq`b>RIPd3+jJCl;Gw1;470>q zbY`s6!zjkZksxJt)g5UagGXGzn_cWQxT|EWx<^tLLNkR zo&93)I_I-r4N|J-tVr{Ya92_2A0I@X4bS@(MOx;I)<+Z*Z=(^oZ>di~1@*J8 zl|a#roU41Rm7p{RX~!X3$r;Hm1lYZ7!Wb;NQj*6dO#`|GdL`Bn-mJ3ZKsyVW_c=a~ z;f#_df#n}4@I^ib!l)#J0v&CduX3ffJ3+>K#X|mkN0hizDLNdlgvA1#ALM3n)kbpV zDV<$hT4!donNM@DV7PZ)Q+!jn!p0kf^Zg!q_7Bz~ac~jF{qtL1mf5xIlj*i8>%gn> zPP@h_eK8>VbtTl~53Po4?Ct~duUELwRGJ8{kvQEqIGg)_5ErA~G-5K(=LTKhK^5X* zvB-C<`iJAC$-&WOc#BN@fQ&ggO9iyB(%G%jU!I6W(b2l2V2@jDi+$f_y;-UQq+#i} z4(=kX;2~T`CY0MOP?f~!qX>*^Q__q_x(Yt6N&AN3AJ{w(?GAzHR^Dczc@vpD1!Ca7 z$H~(aX`=UH)kuE7mt~44VkExl(f)5Qtho-8tL%jeW#xt^MNugo=m zDY>uCOgggH7UzcvgXPrB$ECFqcOrd#@08TP!D}v?yOSX$X5U)OC_7UDjfQ`+ zlFUbN!_pCA;jV*OM~bAxlf+`T5!`AF!(P8VQP^+}IvV~T@&sRuot2+%^PU#~r@q+v zs)N1q{pP~Js$pvekBgih6_*=yPt8GP+ZvJIcN@bxF;f-+gpe?ito+>5Geu{YWkXSM z%eKM;*x>Fvpv5PypYynsxoNwee72;Ps4Wz|b1`wsAP>=z8^1%1^sVS|>IO4_Xgh*D zP*Ouhg$RdSFa+t7g7@RyyLWHQ%<`|2Fc6QkNJU}cI*qQ~OIb32;{YC1sCK>M0`Mnm zKzP)0wjpm0{p~&8fUwU846YF_;`WA(4)0MWNG;!ObdYC$4hkM!b`%= zcyd9|U+cbzJv+>7v}YAAueRvCJKIPCREQk_r5K8MTZuk|d$An#p=amqwtp2|M*}|T zheeEZ5Jbhqx|e|acla51UEzl~Hg8Lo-P(XHQM}JRp+NWPX3yr&((O0F&3CG;NH9$U z9D+`9I~6MZ9e1W;-VuQvVk+@9A|fK<449ZFJ2FmJVr%snH)eB9jr`#OmB`FS$YdCX z*AQB%!1`Rg2gHVIHFfA_$FBH{)#K`bx3Y9_ZqCdh{>gvJjFSf)9^+05bK~f1q=xU0 zlVV@ohqh@7ijg;niz+={tS5MTRsVh03iuH;s1`lX{s|PwzyD9zh44PAMUd=fUZeBI z$nCc@X`%5sV%?LxJZbfCU?eGN7#h$Bu5+WIbB!K|cj-U0Fs?8BC@KnTLrFyU$0%L+ z@Co@||L5o_?I#)B@HO8?Cf4@R6GW9@S*agdstPSOQdF=UW+sx~i;8BU-ctOt*8crB z=3}FHa|mkh{hnVpMl5NHMjj{N?bEzCAF274j6I?3SHQqw3Ko%#mJgG_bo;+U$xO+E z?=iTI6Xh!$oF;jEU@lSp7o+okKNpR4*&hMlUt2BedG$Z%6$qG3WP77*w&~)0E97>1 zM@`w~#^%Z?>aog`JFO@^a=sY3UMg?iM%RED`ke52nsOy!Q5I~T)(q_=980r35H&v_ zDEEn&kG@>nogg1+wk`~gkT);3ZuX95?L@jL-f+)-aC05FoV7cZ=@B?jUQ0K-hk}Cg zje!wV>y;j&OMox_@31|;_VBARbI1%ccPivT`@e6Xpu8w4b%Yu?EaNgYy!_wKy&8Tt zaQ#0Y2%jNKGn-F>g2Ka3v*qHUQDe~wpPb-n~V0fDa6pkeiQtkc1Wq`d#GGrabJi_ z!Cmt{jLGn5uq;8MYjwWR<-OC@(v#$yaP#BUXI41>^IOr+?sC(5p46LlDgJOcFZ14M zkK&B}21b|L!EH5{>nZaj`B`+B?~}%G z8CJ-2&oyMyq%>)rdQ?s){_lS@cu4g`O}iD$=2Zi`92E0#>AwJ$$Cr=&t}9U z6Z4|#xF(VXZO7u2NrI$=MEhbMlU^=YI~i*a7ER($n{7f{VYo95*LCYXm#Ed(8y3)6 z)!@9om4x#J))|M}r<+SNgYA^XLyh=zsr`boETso(M_Qkt0 z37I8M#R{LQ`!_1M>~@puR?~&d59zJ&z@&c~fy<=?DG!6s)^RAqN2+U~21lme2btzs46hSj6a;j(gTVosVvXrR@}-J%^}aHkH$rtacYS z-EGRBM?vwD%yR6fO*YAvs} zk~ddnc2dz?cVhI;vPR2(9Jhs{{z z;mERmp3cZe*JA`^bSq%`dpJdS%{$LVX!ON|(#0#8uXk0$j4< z=bmABO>S^MQ*J1H{wF6i(dqXF>43wf$Kf@Ble&syEXlva@apjfD^7UBLc(|FP-nA% zy0KGc210l=Yy{2-J(?R=+S*XE^Rm2Js?8@(nt|h@{>iC=_KgtFq5BFBWS^dF*kbkD zk8^o8yoF+2<`p^>Grukq(KARCOuluoyrc!!#r1q{^OXY>F7>gA)yUXnqvD)=)uKiMXj?AB6Gr%UDXl>2o1dczl&Gyr+XH1yvvWq`5{UIQe0XFe6)yw`B0DR z6P}+m872;*{X1`OHj}551OqmD$iFl<-e{wuCXi|2H8OTYqdDfU!#bvF-Q7dE;VTHDuaN=9_oZF@E z6mcZY$MoYCI$6u}v169lI;x4{8wtY2QS}WRbTzu0u6MyGw{sFA-0(^_>PH%j4p)xO z?Ja`Cp4ktO)-~Tg5pWsVdS-WYuevSu^Lxwr>?3*Y(c8Y(ppY)8@q>TgcKRoGNR$e% z782t8Pnhyn|Odj=U1!IM7OVIOTF(9&tgRa5eSiCKVmb4%H-f z)&v_vA!jcB4)i?(5928e^YHRoquJp@bk%#`Csqa9jOrC{I6M<#{VVgb(-suKRAo6m8zHZPB2o`UO0I>Z_O!ns~tU>TorD*ibG-A$*MnG4k^fkRU< zN4Hp0_huPIm>XMZ`W!3$h*=|XAm4O31&?kh1#oYynp-qQYS^-}DgcSa)`c1U# zNa9B$QM#5`Iy(CPdFsCNzPjZ%r`I^^oaWYs)&|$?MGr0Spm-^aQB$jgZt*z46RWL$ zx3TC9Ba$^|Ubl5{HycUhlaLS9RE1f%M#GsIOgCCRd6RDaJ?b*uJb!Yb^u#FdNWOnR z1Y%Gn$a3s)9k-$j#7+TVIBi!QgBY2%@#RF*)_BROS^2H6nBJrb%{3m-+`vF*25%wO}?0J*r$%MoX|lyUR%YO!itYK zPUAmnL_W#>Glyz7`|^JMK#A^IDfd5m4XgMV^H;?zHLUQ_^MCf#n|E7 z?ZTgV{mQh}UM7KSK>1f~!`cboPR;ARND-0sxs3kAqenO`06-+m(s&%nGv$eBECvmV zkfuK%Wn_Zl=v>ngy(guN(AkDbG$R~p#*spG4s8HWx6IS5F6MEw1_`+wP%5Vl>LL$~ z5g$NXqdy>Ca0=D)t>q;p6CRt5CwHH(#Z467XIF#7AR4l1{;6M0+l>0Ecb2B@(7P1?$PFO#P+Bl`!LQ#kZLGT=sNXiP#_=;R%wud@% zKXK@YvR$^zI{!3#dNy7o;FqMuYzTFi)p!&8rBI$tP+hZc+&c)Q;jwNlz*YfAG%2K; zs!~l1{#$>karx;*Mb=FGaF{(^AC9T|JL>6ri|P-W)#oc%edkV+xDnpHh2Px5K79N* zTo>xy``J6)iMFu3Iv5x-hlFGW#WdBC8E$6liYuhW=UOa+2Q}2U4Q~=rg{EuSdCYeG z2iFzHu`?>_?E7@nS3hw<`poZ~ENvwul*6HWSqQN@*jfs=qUZG1z}?!#vX+&cy7LCdUCEKD{^t4} z%EZc{kJva)a1^-&#&>$k@}S%|x1{MJf4(&sRS*3wV;cv^-nawsbkD#5f|=JD;<#=7 zF*XQOXFJ%>5-3@;f(w8{PcB!JnO{CQ0K};F08njyxZItE=Tkno29)|mKqJ$aNau#& z-#*aH#ro&=Go=p3)qnBxTVdv&-FmH@@*;%PxcXbD5WHlYRACb@6;||(8!Tn+QM!;} z+vo~SNnIMK#$z?2^GegaN(d7x@r00hHJYnWwKAsv&mID%jQsCrLlpEcC`E!oj_dP_m5H}Zepe3$0sB_>#gLggxam*C!?^X zCD+9S4y`9NB~&A~h3G=phI^ab=bS-L({pZ~JzQGO5FD8{gtl>g8 zHGX?ugegsrG4uK-pP12}l(@X5$_HN+)| z4$(c}MsUg0vB4^KPVYczy8PG=5xA}S+;GRUn23cqp^=<@i{{PoW4h8|n6^jOF6K6w znyuMt?scsosd{(4wPtYcenD}a*%xem1A`euhv?YYY@j!uO|CM&t^=(=7hU@`C!w0# zeGHH{^{yvor|G4rV(7N4rTm#W=u#!1;<&S@Airf$5A9QaM}$L6lS5+p2+ya2I1h8B z`g&PYr%=mnAxYE^pNNwBN^G1?%+ys$Jg#L(9IX_;ZWgE~GxKG2s+(!T;SyX9Z3n8f zH@U_^TGfgh-q+kl=v)2Evz;*poSAEE-U3)=pOwh9t*^?0Bxmx9AS0D@<~fNasujL< z&s{<KmntaV zef>Js66`7`n+K0@qRB56ZovY&>3MsZiVRLS?*GKP0LU?|-c+z@0LGurY|6?YJ*+R0 zXOJXD5E4*ky&w#ot{EcDEc1PO`AhgE2uC#=4O^lF~pb|`ThGk4&#amdh7t-79**L^Ge|-mErM`6bVqT zqW!wvV=!*HLjQ7d?XYr0mf?6_!?bKLTKV0SszW9qq0uoj7fX|4dG+*rG$-bJ^nvM% zqWodKEtWFu_0OUL3dmOj8so`8D3MNe>?$Y)w5QQx)~M_vB`-6$NT5cvstFXJTP|AlbtRy@ROuz~mw({^iaD6%(V^XVte4TSuS+GW3%r`kYl@?R^J-g$$DbR3kTH|1J8)Y3Y_?o6^}{0@v& zY44ErD1(iR*-nS~O&sxWm;SmNr$Itjod_$A9rGZh_B7UetY*u8?M?N)wYwX~!enx4 z)!iE&HSDxTOUXLWbSE>+w5s+*_(bq3(b#?Ca_vkeo6qcl&kg9FAmWc6MEqaA8J1X+T-+qFUA{(g+3B zybU~xoREDs*W*vBWe&RAj=*$M7$56~1sl8#1qv9q)K)BmLAVA+vTYCDY+ut3gPuY4 zc)gRARi!`aNa^^928Oyr0_Zqavvl{;cEOK$a{#i(2)kBsz?@LSDgm?_-El3ue!)5O z3Xr!|z0;j9M%YgOF7yf8qL=1(!cJkXU;dBAr)%vR<1=u9BNy$G}N(Jh2&;A##; z_-2ocg8j_4Uq>-LhrOMr9R@uRJ(?jdC7dQ)@GU@K`z^yfMUtS zsbyedN@~}~w)@L{B}R1DjZCA0-)xx9Q@O$TvFecI4JzkJ~-hb;fj}I;q#mm=d zPNpa;)K6^ta+SoYA)RB8eH>b_S^|!+yt5}d*z7GbmQ^AFN#ZgdRzjho3)vE?E z=8Se9?Vdh;YH0y?A%=8!Hx zNm`zl@Hs8@9gcz5A_8s*dvwknlq7A-@#Y;EoPdO63+K0=#bmkIApcQ_;Mu${9%e=3R8 z39QMg(J>PkE)Zfpia9;~=5e~(taXT{d*YbN_Io@X&|tV$}T(3??9%waWZlX z)Ug-1DiT0+6o{A_x9#{ry4dO4KOxU(Y>2Tzrv&3`{Cqil4NR=@3H{oTRF8{p(nqCB zIO$7*xK){y^_R*#9U4+-3?pZ#x?6VT?mgX@jAQilr8T88dV&lhW+Nh-_GJX>on*oF z@scGP_bdt@9s|`_nd*a21>&I3^qS;7_@uK+y0LEK+|COvP>-k`xbC)F+o@dtpbYPw z%6<67H~fFXWYN@@IOfVKOuyv_3jRFf`;p6v&~^YeRl7=PnRTf9TI1Pb@aZ{e?P~;R ze{wSB4oj4kmbORNrvF*iNmc;*c);FNO*Me3@%4e4{IG48IqP58#b%+6d+3{g6hAoZ zlwwxct;s^?nr291aIpN2}>R_K_?YKU)uil5gq-(+%&U(X(aV0aP^Yx0HZaW!dLL` z`SfnpTRIMj(!D~_%!SjRL1LqX>qZT{pXL9wF zsPdq`B~-bztk%{4l0Y6(3T-zpkTc7Y1Vat2upM!Ev|4F4DtUX>DP(^^$gt=evC=)^v6OZ2B8>##QX;*W6A%%&9)eUJZ;tj> zVb@*m)}H`wVLe{Ogpi8EO6f10BXKlj3Ia~w^^>c;ah2#x?RI&uB$XDvzBhUS9zG%A zN{OJ*|6kuN{F)60sWslPplTFRgmsvq%EqoF%#wu}u1cSuUaq6;((m@+H`OFieC zKQ4IqiQOw!Xvbqv=F>Ak-&h??-3tX1qWNlt1^tgm(`%Y!`$*>6Wjry_d$S8Dmygw1 ztZ|5t5y;$Kq|a@V;?jY}QFzh4KJSlC`_dd!r*Tw>Sl9a6u=bJq$tgk z`P~o}U@}!<)NSS$^km+B2NK?+RpzSTGl5m4F-sED4D`&dnrM@toJFOSj-?&#C&HgJ z%?<2mm`ZJBgS&*PT5nID{k=z$@r2LIFx!b@%X+%gQDkVP4LUC@%6f&j4vSU6TuLq4SK|3XhtW%Wauq5XL-0(Cb%DQ%9GMDlhcFvL>_SgH<{NO-CzO-k9tfjzBE}yH zUCj@W(T9*SZUC<)!h|U1E57XOx1a!bHa88hX_V>c=w@?2`^omTdY8?s$P<44@#Dvg zca5)6>KtHT5ua^qp1g#_=hy5L{WB`og@py?Te*5isFQS080uFaPM3m-FJI_q)kqG1 zgz?ckF|#y(*2(R-%hf6us$6X2o!Ihvf}Y;mCCrG`XFN~%JRu+0UA7ZICm7>q5Zbt* z@-e$`QeFMv;23)H4)AhmVbKxvH|-IrGPO+|ESF5zfn`^< zHJcXL_nUyf&O=W!Hnj-~k52x+&3SD_KPBN{KJG#Z4Wa&$1?}<NacJ&ZN39De7Qu!Kq^-O&_kg11H5ZB zQI?Eeilsg>^Ub`iRMi@xM)tyRUcvKez8WiKMJiNo$5qXBuOFn=yhnW;+D|hMku>jV z&e(GRT%GpTN1ov|fm&5i#Q>kw-;Ee6?>9RL3IL4l6uiJ1$kWH8=@c7qF)PeQ>7kS5 z?IeUsYW0*@v;?Z!qqA|tx$NaE0CRc*!z|zABb2{0wmTm7lAv8S({wm9kVn`9;gZB< z9?L_Y3n0~Ie{v&`fx8=ia4p3?G9F{Y+kp3aD*ktzX_e~M9Zi=&pa)O>Z8SF`qHHc4 z+&`%gC(14>7ItJ;w6!eIb%5vHTBl!b|j1P&7%bp5A_=Hkb1uwUG$YolXnf9>!A7+4iYk zB`U`^i@*|QDc1-{*0V+K2R{V3Ehgl)1IblRsuj@U`#zd> zIQ(kC?syT?2%(w4qqkNiD(@aRM2pokr^Omq<4W9?HpeGyn0@0|YvIVCdFxC>#Vh>QAzR6K${!KgKvnwg^WA`Uw$_6pOj=)s}HN=)j(LhTGmHMcQ1^|-r#7AIUfWa zch%m{wuc=}>UUx?m6QRbuM=ZW{_NBNW4&e9fP>(U>=7O1uacyPSQBcc9WpOQ>yg}ZS2m=%bU$T!~0{@K9Jc1|IwIRQHz|>Y5C>(WRC=n zzhU(fsK#Wn=A^jh%R+WIttxpghns-IWcB4SL(wHzAP7nB0}x5DzP^6L5E*!}55e11 z5mI)L?i|sqR091=RK-dubes1CY>%9?`na{oaU?Y-}_Pl%CTi{ zroC0f>35Gj5R)cbky6YD|9P=E)t__(1*rL%Ud~#1Kkd~sG&HPPa&%Px1RsM8 zm3*z14xFw@5)KmS&5tSYQG9ZFsH0>QBH|tMTN{|Z zSYBFgJ5@*na7JB3tZc85s5mbGvsl^Nokq9bu?Sc^>27z|VayWWFUaAf^S&EgWOD?n!{xQmk+h@-jiW2^wFDBL9 zv}Bit4v-2|*OHx53hqXGZj9uFTQ(es6PMDx@BN6ydAv2g_wAAIw9}lsUsDP&60@;- zCstJ4jdT~7%*_pz6)vd#q#MDv7(h8)Msqn}Nz; zq7C;|w#XOmqGnXOpPL^L64EE5qMpzriT`)Gp%>3}eW#!hspGOjsQR`oUsH`LXfas0zB5^ zx!arS-zZ9;w+QG8^Fi$8-y!xyxOZ0PcOxwYjml38tHS3Ia-|((jtwm^=q=dHCad_W z_YR60=wGWtR(^8TSF35}wnXBpIjYAb=t3 z;g$|f)u)bfY)bk3(b4%BVCmk6`DBd%@@T_f!%;~Ham_$QB?c^*Ci9nezp!~~IFyA) zMZRK_WP__2QhuynYrFDt=59XcL?9I))sBJgIG!z|!XYvHRVfoIt-HOhHpK1pzf}z7 zKvxm*y0sLy=}C2r^3>7ho0AfxTyXkMMTVMDyabucvez#zQ1V?*=C;i?ULqjGKm$cz z!+|l8{=EmUtwBtd`CB7~AJhUGOnRyb!U>7J-H6_Ss50yRlGKYz1YEC_+8{3a>G$4g z=$*tCEfu{7(-yK}A0~Odp;+T*?%ra%7dI_uIdmTG5J79VSe?A(mx=UGOCR{D7dETT zrb)S!UG}?ql>^;mu{I&{tV%oxbejTcyGf(CT5d6<(*nFx5hk66@0eUhZ9XrYW*p+g zL!QWISj*THMHT&&vtja-j3WI~cF7RmLQn4*}qMGDe!S2;rE@IhV2TEUZ(1rf0SyporZaV%Txnii=@u8=Gy+)4Moz|h1F?lIpJRne5e$@? zJ;#@o5Sr3tN~1H>Q}UN$h8NLSMOD%H$H4j}F3kfr3RdNp=uBOA5Q2gxUV8Zd-mRIueCPFml|rHDuK~9-lua6x+c+k zs|3@LD(Rf{28M40rd*y=>)1ZCcAxhw+-z%J2SVA7CZG)h-Gs#uA`l|IO&AqF}$uZ`>EYHJ9R-J z!CO$LlnaCbNLkN%A@BKGPIlk3=h-?AAhWBo!f%93(wy0LFwFcLyBFPyy>PJ3%7Pa% z1dMMPi8(dC0p#I!p3NS}NyKV)dW7@5F*RN%DS&_7eEH5AXX-3_X9N|o#mF@_|H58z z5bLW|z860XA#@29+1H>ZDnv>R@0$TXM$p$j(|e^w-?5fBf`@|fVD3nQ)ybdd>o(pF z2@1rGbXQNhL_i{a3_7nhUqse+9vakNh;!%>{O8Qiyb7Sfyv+BW>Sc46OEs`G#8^LH ze%I&xrFABJ;>kv-Og%q19Gt(Mco|$%I^cNHl<|Wzf$?IH_NxZq?PU) zJ+(oa7L3`;=1G^eK|e}&r0M*Q7%(vd7*9?gzp8GfqaCGW4&jg5e`x?QXLjdcqwGj6 z3qAi`&`YH&BwK zi^;}KWTaetpuZkq4aR2SvSi~GNAh%qC8fb`bZ4pqf~tedQCH-QM@PNyB5ez*0UHHe z%EN0XFe9U6P;#)lj4@18kdyC%YGDPWUyfEGYh)u-^N;%w)Q@fV&&q)#8mPHJuYpUq za)=fc0%{vPAcsd-%w7_+OUX1r@SsDy*RlB+|F4%&Spl@sZ$uPg;&fiN5^>rX8qx>{ z7zDx4g&0^d;3tYlJpglH0DC>8N~EgVG-BxdNkXL#^Vtl|_lQ-G6*o_VTgz%?&r1Ck zMyGg5yPY2AZ@;w=N58C32XWz7=}c5=$(}QBYhFx-y~5z}k#;c)V9_YqWElJC6dtE} znr2uNDFzCGiTF-ef33=DfSIlgX7qZMJrW7ou=c@1z!z4ECC`#d*YC7*)dn^$n z1%Y&IS*B|Sqo8;XB*BgJmSKk7fScDf@xPZ9qFDeQ&d8SU&=AyWq@cmy+_KQvw{+yu zJ>tQ65}Y4ZRbF9-08%vyv(7I#s)`KI*Q`48!B}m!&Z4KvW3qP9md{T?#8`lb77Eyd zN<|AKakNn=kos)GY8DUYK0Mgxe0GD|spZ99on7$ajj3n`{9*_Ti70lg<^np>pH37oY5ZnVF9WG;Wp4=|-x|&}0jlaJA&bQBY>$ zANhsMDKKhHC$I4$4@eQyEQn2>(Fo@m-=!7y`U`DKL4N)Ej4uS#(yrsvTH@NBsbDI5 z<+^T_Gy9W)jCaws^;#!oLmxe0_bOCP2>8L!E$1sP&u!MgOS%WTC6#0nngs-(koA3{ z@-02p!Uq!$2ge3Umj;&1XIJ0LY6?m=*|5G;n;NLSmEA!7mF;}&kt};R1L_MCB{em* zh7@~y42U96vBq3pfLF5;vXAUw%UDw{XBo=4wa}z^{w^d0pd_OwLI5`4aa982=I2P; zZZwZQS;`d;+BT;2&Ru~T!m{Lnp2!h=A^7TM=J4{bWn&{u=qxpB+%Vt_K?6DIAgr(y z@fG>x&W-r@0Jv%WX{lRms(|ZnMQe;-6D%!lUbHGSR-h_yUCGLD>=ijMoBjGe&)>yK z9W1-$A~$QiOR_*1I6WqPQU*vzHOd$Lsr8iI+$w3c#a97Z;e3~2En_SWa+vjLWt^wL z>TZm8DE5gs|AOgMMtBL|k4HTkB9b!G?&09(UcUke!_@H9+J_&2ZL}@LyaV`0<^Z0Y zx@yu*>Nw&&KP#@x`>F>jF77M9PLC|=1gL}F%hye@VA$pcuiv(bAsPhNlr}`&{+C-k zFW>b%*^#&Td)ZKFs^r3O+nmIA7{4MSKC1F-xzAxXTvx6W4dc7=goxziL(VV7Q0_o#;s`N|;O;I(y&`TK6~ju3+=muP(d|yxa^R;mXx4Zix}J z-8TdSBl35_Oyz|Di?t@_mRqkfL2$DUCQz=+4tN65iejK0(R(8E{Mu+lOi#@*dwS`O z(d2x$_<03<#t;u6Dp&0*t>*DJ*P*%z+kI9V<>^4YvNB%wAT68>e%3>CYCH9@`!QGdRXymU>Z19;PLq>%UBAgh z&wT~CSHZaDs>|lfKf`J&0x7Y^?^rxOXZqm+GG9dq;A`cz=Tp3zvW@81k<#@o=nQ4% zS7gE|%YhtuwkVlEqx?~jP3v3AWrP)Mc7YnU;~I4{X{ru#)C4hBk|pqF+eoY!3}wAv z)Mx`d+nlg4bvsLZq`mMO!M!`_4J6LobjEk1!NKMn+;&j_-akeqyv1Fjao)&X#?1F> z94TT!VDjq(?;ao1lG1Ej1j&0Z$sSbt7wB)rsuaH#rgEf-Kmgz=FX|9P1l8G66{PL( zejNas_5c-Vq4sFML3klTjnDSmQ+2^BX$JWpyIn|XQ)0#WgDCirvvgeYT&u$4se%-( zY%?b>(@fm)f%7TlOa0LG6Lp!9D05-WhT*iN@=g8y1rey#jb|Bg$Y=|59XjD3TlEBF zW{rN=u0Zpg+ryYE0S|1_d1F@NT?!{hYN8q7H{gejt%5Dn!0N6aNNn`$M1Q?<*Kw^? zOUU0BQmT=5OFPF!#!UD|dEwKLTq{dMTJ5)`>qW|apnCeVkE+vnBo-5b?qS`90`z|G zud)>$S8V~%k&pwSXL6OKxOfFG5&?OR?s2Qw`OLA`sHV;^LDzBc=#_fX8%l9P*f`Hu zCnM*sWCL0Zg&g_Ss(`ydKNCG*dN+o(74g*dttI|}+_1%S5wr<4)5wJh(jFs|-(c(t z!zpFufNf-Tt}i%wrfoD8Sm+JHz9=ds;BB|o!PwGFV2RYk_a#NZm|=E86#P7PzM z&G~MISOub}8BcOJ-;Sz-uKxA5h6QvSo#Wgs3-^^v0e~2KUGq9Q?YPPCcKa}V{%h^#TP=xEp%T9q zs;R;`rqYDTVZ*od=>SmgnACU5HJZbxgYKpPB)3jxFW4RQKHp(*eh5hgb5}R+B!jWS zWjKHm>WHrxjQF$zmF_S~p>%2nfYy%Y(__R&O>cmf{(IYFO)kUEc;0Z#Q1|9K9MX;9kI{vYG@)qGrI?V1w-J#0d5{hsRtc zH<9x-ujRbn^>Fa$qIdBkZgSEAv#7Fq~LvnaRQJmbtlcoUEu2DJq zEaqK%;(>xork~RAnI(cG<+~q+;tx|Mf$|n0(98{EH$Jn0 z4Z~i8&n%rZZ@*zDp?dv=$0QISJsi$-y`h*P(%9j{^}P zl2v=Okhas(Z$DQN2$SYOc?X)yao4{b+%W_M*ohd_!P7v`3$^FR7V2MGWa-w)gg@f* zN?qFq!i`dna0G3^qft4?d1(WUy42m6ErHgG0=Z9aACzY&8_Zs8ufBmSqtXT*5$tU{ z5I~1A?tD*r9P;`4NSni?h+%m0SH!md6)a|ygokMI3ekaeCz-VCr3XDDSRgW7IpqU8bs7U7R!3Fp82Ycr-yQVBBiLbtC*FAa6R5)%Z51%ox3m9H`hb0k z<3_|@p61t+JD^L*pMtZReZ`zPj;n?9Ek%jU(1e#7CN0X-Wi@zSG4ERAvne$*=apW0 zK&Eglz(W4Lev?0qvxEL#oo6shx87&kyhpdP+M@+rQ!kUy<)(Uy;i&>p6%f5)38J3% za;;P?wYI&$aui01XNv9tj(r zoetVU+=m7*^Nu$#ilUEAK7PNNXP`FW=Oi42*5cQJlvB{jB-&?TZ~aWhX^@ zm5cTj0XL-f{HWJ^Eki_g5bWzUe?+D8W*%r^xZorzh=12k(N+J^%gp}hQNaZD zQnv<%5@?T<38UX&Ey|r^9$0*P)WBwO9px_e{s@h;lpAOpm*!|6n`?E%zCLMTH;gXZ zEV-lr6g9KC7rl{1>8BQ%E!67%tRgbT@$y54>Y+n^mp1tWv+9e>6tt>1nGPORpUuR# zKHT4r4=mMHCFirA6)zJ2-!wV{bezg0d?J%a4odL-6FgiZ;9$FUp&rj2Z4?{v9`5O^ zy`pd5&YX9wtXc4M7`P&40h_c+ioY9#7nCdf(ES=4j6>vTKI*E;BB6cg7w8wzf%ZQH z>0XsU%TS)KHo@gT?_=JSX2$Xq9Y9sG;s+vrj9g_-zs#R9$2U9riDrZY{wZl%Wad-g z_Yw8yxoHvpPDN3OiDI>vnz7>DibE6O(q_8~x|C`f92uTw$o{J}XIeiVNxU6XA-w&3 zP4c;u`=-H1F4q=nMk5ehls!kka02p|I+(v*{UxmUui&^5+dDX}9z2Bwpp+GTfvF>9 zc<6**TS@dN6;D#=jwH1p+y@-;sz`}pp1*6hm9KSbd z`D~_;#>LaaApbQ3IiIIeREfvfY6q>`<%4k>5lA09_67}Suxjr{+?yD-7z|rE$R=MS zI(&KH3&p80r8PN6Fj9ICOz*6M*O)Z0uWAr~1MCKO-Vp}m#}tqT=5@#~dkXc{hw1j_ z{RBju3j6v}&rCXrzhSHTq)q|Qj#npV5fzMa%hW5Jue((D^PU+V)f?`ZNGMP=Ia$%YfvBV}9v9O`T? z(U5h!Z%fO2k+w$q#()v~KcUi>E4f8a?FU?yl?+z*vnzb%0Nd97dfnCY1#KH%pP)pm&MDiyGjAu{aXEq6`5%sxbC4oY&<2v+R>lZJ) zW@q{zazjc2^xkk#xn#3f+Aqk0;S<X3AkJd0c<+e%q;091t4ZO^D^8h%`R*U#< z7Gute65O-zy|hA&V=Sa0cL4%m^pn99z3>Zsfg-@bJLD?hg;YS$`FIkxy?TKrD82zJ zFM&3%kRat%tX+WH?($4arc?)qj#_)pWC4G<9h3t@VojNnW>j6LnVM!rY&}m+k@Chm zIx;S4sM#Unv1eGz`LtO`wZ%t(z-PEn=YfY=pVZPw3H-)zAR)^J8i&fn5H2u>!Fd{I zT?IH3#JHQ;W(Zat4Y_2{*-!^^2gwa%Jh;6JD1YB=yiZ~P_9Rzs%CLqhc{spTHZeOU z4Om{<8=fRJ@ks}xi|h8|y<^_j@gD`Q`lQT(%a)+9-^Ewq#Y^IA_4;4}l;8`kNq|U5 zw9@m;&=i}$q1H4Xdf~p(PvYMY7@53ruukNNQX%}1e^MSBI3}qNL5u8 zr6N2q4g{zFyIwz+OZ#)=rHP3-NUvoT757c&WmU#C!-pA(urJPSyE~i6zSxGm58j@A z7$d*|DTf&@K5?oEsO6Dn`~E3VkWG)kh9N1VwI;W0T2UE5PUHz2`i`sk#N~e%x={94 z_JO+f6Mtsk0_N86N-&d!l9Gri(Jk}qO!RS5?^xgspu(bL#M(c)!kqpd*Y_RwlIxl^ zC6i1{16*LPG-U6jZliMbDh?`usN>lrP zAg~qLTB9wc63pF5HOZK`)UQ^h{hGQ;Yzql*{fYT{tsG%Gn~gTYCJh%|Npk=9QQ+T? zQ=#bB-5~hwf+Ke+zyg8G%LQYVGBtVgJFcML{t{e4q2(+J=H^qTUhBe{#p9nd{IcXG z;aM)~cA?zDWe!9Lv5edZq@<95?zPwSplS{bDpeb`zg z!pi1qT`p54Z?Wj;=p&h@OsOsV6Kd&Vzzb1fvc8pP#~a1YRbqh*Q1yyW?iU~(J~Z*M z12^rat%PzD$(VTBPgirkxfLCL@^by0mGCz1YPS^)txL&o-yWSCyJWp7AOO>dc~uV# z4o*I1WE79mv45{9^!=WI6omXY9S$WBAX#z4r#HYohrdNn3)%$+5p$9;6SBtLZ=U9X zicHSQVXM^xvc0RzrU~Cbw&$!?PNV9++dp`!77_3H;6UqXcmHRft%lWBlHo(jjoC!w z%Vu4J1lU@nfv`zvi`YXA&*ukDiq*%{qEgp52%?%V=@;{4?GL8rTi1%-6qY^niVZj* z6MwTbeMtuDjD`=j ziTzPu*#}~{Ra+Zqc1E5$Sye2RrbK_uw5zQOe1d!(?{#1jQZA|&H!iVRq3i6e>vXiP zw7QP#*oPD!m^7Xd3_*I&q@~K#Clpfl_6G@kVaVU^`SGPA7Et1sg&ZEb2&ly451fRb zr}z;ZQ^5dFVx7d3Oan=X0I2zoiV&PnueZsM%gn&cj!8TwoB8Ljs)ddh1kR_fF(U4z z+W1;3SH;uwbgPw8yI~P-u)e}h^7+z3j_|FUI|*iVl*0Jd-| z4+6^=Jp2RHbGzLCadu$W1AnlwvC$FFNl%Zl9!Pxl<_2Iz>i+irgCv%elBVF1Xo3>H zdGf2f_S=GiQdrvm%CCz2e?NWQ(M4!^>Q#rQ`u}^4G}%Ld0^r{tT(}SjY!H6`|NEE! z)fMA^o_G^qoBVsdHu}Zd|Mv&~Kl}niue#~+->oDrd}N^f|9DqYBVc}aL~QID$$yqV zzd#En5&gu+bZG|9`TuB3|GO)&t*1vjHC&)n`T*E{4dWLB%S^jz$`*gswH@PYqs5bz z+GW<(AuAkr?>;lc$Ru#z0WaC(V(BM ztQEjx)UN;hI;OvWd$MxFV&4q=$R(HOiEAKAqB_Dp#m!9`K?;qkFSj!4t>_!hzhS@?yMg;ycnf8UjKG{6Z$htCrGY<-;J z)Lwq4a*8Xt%cQq|gq0#9j7#$1W^y3|3%<5_BJeb~TKBgPe|!V%Mdk8Q8QBq2+I6P| zE>BAB@$TEHH@e;3a{_**B}g$CVPF6D7hu$2bNhF`AkJHDMVrXm?tJ5dH5TJ5O{j?r z?{9w)iUf*&?qTlFDfxAy2is@cTvHI`{xJ27O9IjSs#y#Flz+eeMyBu8|2U;2DtpyM z#I36

CS+MQRd1ivQfkZfwwUT&j0(F-+$j#o*+B9i96%t5vvoDChsK5lL%j3hob z@6$DoC@=bxmeJ%M$M?N{8aLzklaL_wI@-60-LHbOqxn^e123G<9+SA4nA;*oACqh^ zTHX@meZ2lM12J=AykEQNa9nwEo7X9;nng|4%d^1w?7HmkB00l|LyVmr6T!hhbqM~h zGdVdm!ci!u1SKM}12#_iv05hWRe{an{kTzUOF3kyzUHV`nYR<(u_bxmP&!jG$n(Kl zY20p}O7qJH*odmU;lwK~Y+F%KfPC(J&yqd&3X~$P)_TtB?t@Z|*l&6~iTr*i#D)3U z4t%~s-{ihi^AqMLVmfE}x6y2!)!zq7tU_BRMwH4#ASRn9J&FzoA`54NWAneiE$GX{ z9fuC{zL4wJ4aX`t5GEH-^e-5Ljc01T)L!NU~PMp=<8NlT^f%_r((rifXAW z4Sv1nn>6|U-&N-yZP9EGU1A>b91D9WX}n3B*wgQGrn+R_B__>ouKfeiyqYyLWmWCr zX=%Ii*~d-LvC7B9` zou}i+<>l#am%sXVr#+PlZfSYeNoW=E>(u2D4;DqxME}(G zn>zAHVd)u3On|T1p1gHuyOE7hoBXTbS;dR#a@lNkhSMT$r!wcDD>mOb-fWlLXSW_5 z9L{`b&hTOQ-K{KDmz>L@MT3@0)F=bq#LC9#uBk7$me#9;ZNGPkh#^N`^m4xSe zN2x{xMGAiMFTAwD?#m~HMf#b~c7+@-k3*SlMu6&e2|f0C1=_^|Fi*EB7G-2v~z~ zppq&EOU*{RIJxAelJkd8GqpZJs+B`-P~GPC9}j?4Zn5P^8PHLsTOQreYqGY8g4?tY zSQMA8c|u2&|Yf1Ojsq`KJkh&TF*LX}BiLp>-$GD!-&=WZApfmrlLiSk`awl4so|DpZyR* z6bumBvl}55HFdKm)9H_RJAT4`i`#24)2p}$)C_Jmzic&Ih8BY~k1Q%a>*#0?6VvW! z>wzC{#952!YZ!RTMIGiaN9do|BR>PK?Y9={5mJX|v`44gl758TWW-&X-=vPc6w+@W zy?i5j>RMI0(P3An4g-2ZQmOU(n$;33=p;3A)aUX|aHu%@zfsoT^L0jJVmSEQ$VYh; zpC^1t_PeHJ(Z(D{sV4mJOOdkL?Unq7{0Af(zhy_6wt9IjxzxRKgBf!IW-|ij9bH$| zP2r^;c^}klS?f@xcGc&KROt5w%sPhf`gAE5gTnXq-UQ>@ zkP>_c&$Lw->$s72Ss;Mzvpkn-||5JvySk6{c=>! zwE)W;p^z9d;?h8(?(q@K`@pLXSW6FhdrK6vcLAxJflu#a)-2#Dy-B-bGzN5qVh7g8yi(}Eik<_lwd9I}@b=gAB zj%p^XMX2K`=OEt?+?p1+DtxN;&-GMtn+P~TVB|WfCCrw%69$S0FyoC7uR0!w8ycNw zAyX|$$B?88@(<-HG&tprL9fP&e5CYF$Rx;0bzCG246IUed_%HdQf&D0&&-_Hn+$B! zD^6RXvj|t6tHyI>*VqV5xEUFZep0XY!@py2-)poha_8S0^Lc$4=Y+20Jfackq@_#E zES|&SC<8S!3(10U1Ib}8#>qiYG5Eo{KTx}}Qp*0eq=e7rw?jl3&Uw#xCYpY{2(!Gz zx86gfo{qF|2GO60*i!D6pr8*%|CUd)FTW>ZmbiQtdNBB7m((s+^H&-R5}4 zn$D8aA71Izmg|AVT{br-*D*<7c$`={!vqW_lggxznE(ZmWOG%f1A>+Nc#m zMPyi=M|;zZEhJrW`CViBg34P|$acM7-LvHbYIbHO7tL?Dj{{V!=BLl!IPKM?n^~Eo z=C5s{pOrPYn^;nP82qR$ZPC%o)^T;xODa0Dhb|-OjT8vb1|Dlq&(>EA{<@C~WTTl9 zQ?#R=fLlJyROVnYpMO4wI&>%+uurgfdaTMUD*8;OlzvJrPRYL5wFKH}?5piC<*m81 zKRQkHzo&ZPg6%``KO#}>RV8k)($bBIoJq|Fq%u`rdr|X}a}ao3H(zPvg$I{Pn{w9G zLbQnTVM^DXQYbYy3+e|nu47i)sjZuNegB{XJGG$DlK@8?++-;_AkIH5+ObFwCGE%I zegL;>LuaAJeaSd80(ajt8W7OsWX97R|M<(Pv_uZv=o6=yy-=ZJ@V0)y46X}x43^EDArmveOOC#TJJ(_@P6&_(-el=i6b6_ zK4Gtz%|bCzvih0VHhZrrMvhqL#y(PHvSCQAt>}dm=seV55>q5S9k5KRJs^X5`1nCN zbUi+)OTQYuK>~>N8*K*n|F-e@_3bF5PxW4#tnPD~WXIb#`X|bG8%-TQxkw!-phW2W%JI zZT3c0h^v-l1v~!?w%TN=3Rl-*bZ6kXJ8)r8%pMg3KO2#(a1WPZh|; zBMLZJ)bx1?OZ7si9wOdtKD_1cPSAU{69FcJu?cix6 z`T;&UhlQ{ds8rP)zZHMLjNRtktNVpFaIh=cvZ7_&ooejg#m65O1voyaK39e&e2TVp zt{#(qtC#jdvdkRHjHdiIKs<$^;czvE#dOw_49J9PhEikygrLc$FiC!KXq(P6XI6(zGq8JVIO7T7D4`&*>G zqnX*hVtt2^N4x>9+VEN-q`usAvflU_p+$}ol=7Q9;120RzvF2<6qytJR%7$SA+!QW zd}WeY?e|X~8DHA7zX6~_Iiyt{+_3W0zkWR(>>Qo>@2)1)z8FI@c;}?;?%r50CoLHR%Rr6-+de)YhLJ!KzeroruQM28|hYu(9vzMNyhya^!yt zc(ibKZP+ahaFSb)QGUJ(!q2|)7pmB6WNL611^Qcq%K5?7Iw|LF=Xx_V#ud@Zp3_)=>N3C_#iFO+6yOnzBN&heB&tuAKLnoJ+@eWHXCS6=)(OR7O zvslZziySyo^gWN-Z|9IzT$Gq?nr>bGaLX5mk~Qo>$$W^ptah0mgmzdZL<^!*&+??P z%7{|@KcG-zbVsfM__jKi$T%B@mGg{%j(milU8BFHQQ(W9pmkO149(n*f5lnKDqVUD z37NXYx=_ayDzAir+I+n#0rA~0OKS()3z8s?WVk}qandi4?pdri1l6-cxVK3 zoxO*TlR@Fb5G(Cv7LEcXQ-g%bQ9+{9vabNl;^8fVaTJ9b(LmJ0mv3UTaW4$_QX){O zG%NjHTp%t3(I3OWA>x$X$mqH4ewMLTiHZ|xDF9>SU7r7+4+H-Jl@_G(-8#_$G~$7g zu-y8`1>oru5(xNg<>ly(MMvi9hY#w)qQfp3G+x9hX;vsy4f2}!o0D4fNpO)0tA?!b ztF8;Fs7`9h>es;Q>b89%m7?=>V|Z`CQv#~;Y`kR`poy~j4W^*U9{teJ7cVNnD~IrQ zXaRrTO2+$H;9ER0tiRvXjc$fTwbI^vItvmLl;y=$OB_qbn7C%SR7YF8iknm=zjb$i zj!jt=J3sTa`j>>(1~Uh2V>L|-<-Yon?eBwgvOYJ3Di=3tp~3s&FD-#?F(koQZ#(85 zpQ@{?v_k!f_|rl2h4b+ao;nK@+Bkskmw?IM6Vwo1UNu0Y7yu3Wbi6`G?DWT*nBPwN z3s0X>nY9Xnvow(-~1VMsYd##?SwJ#r}BPsh%Cl->sdQE9d2aBf|uu z*Vs+!O#z{h2C(RU`3|MCg%aB6c^#QhH~xFp0#s|Ji0cJ~hISu|a6!+OW*^H# z3%5oE$51ue!aN?)p9}PO>l84t!N!`O>$!bGzKtwWcj;x=h-xw?h>43P=o|Ws|4Bx? zBhC-G=-}fd;YT9@88Y09K|gb(Sl5ZoDWS}^w@G2obo_%hTb0*tR)R~Tpg!hWB(2bP z87?mtXfl zWqSn@y|a_ARe?-FEDXU{k~z7~k=esPYbRlOTPMXAahPSr*Cz~h+M1tG-`a*7gzfLr zd6Uq)iw7GC^Qps2^=D>KMSrN}f%(_)JR<)6Mlay*)zQ{wI|tO1t3XzOvumYaYH`&( zN(2vTg3wBQI8Qfwp)<;z>G!wr^RZJCWqMpkSC5@lQVu<|RDb*Jk*;J+@LEni(ytLQ zG*nswJ&S&#Uze{3N=a|*aBeon;g#R;gKhIY=c*EqDXN83oGlE|*vNIqn9}lM8H0v) z6l21JE=)j?YqlPRj-{~9c`O|ky*g<47U%pSJM5K(Jq-Q!OJrnZ%nu%VAx9wlUdJm! z(_`IMvMOHnX;H@DDL+=f-RA4(aj^VHRv&WGBHzlKw;gZ$1L3}YfmH;D9Yx-cHX+$* zjIITadteMpj6rrY0vACkCj?IIfIrT0^bzaw8hjllmf>@H{?O#*hI0AjM-T4Dz#^fM zpv?Rktv2VNVPQl;{n?ds+&Z;paR5Sc*a2%XhBUJ#lQV?fGgQmEora2?E;N2C5QZ0e zY!W`u0w*07I2794%hj)&`lYe?-`zeg@z?y^@_^n>BUMqX&<8QxrV_vyVZ{~G*hC;a z)VC2FH&%4yoU2_7##Z#;T0%Hr8Ax3xORU~fb9|~8axFca)F!v>$JOLWlay@J7OO>wYsfPGlb6U<3S~oU<2?N~TkxG~qztz;+t(k^%mD@{)2VaP@IN4$K zED#8!OeCUE_1TG0ZZabi(?9+J|J>zv(d!T{-Er(-MH~|}KUC?<`NQ=W(@9*~!}m@0 z#B()x9(Yj(k7i&U_+y@aEh=l|@7sqo!?!Sx;qaXT@8s#Qjq$n*f zRgPS|c6GopzY&^K@FtjV-G8sV*$(EK^LNhYGy0yk4E3^eiy|4wPN_tgn-io2wPmJ%<1hxC2M>S&ZQ%Bp~ zjjjKA*yIbdLIh-~$aC%0mdEu}9=c$2slV?RvJ1c@4*~yBm{CYb3-4qAZQeRL!%O*q zU1aIRmJDm$Jh_tO5n%G7K7Y>4$>9_b1$0^2Q=PSaFai~j0BkBuKzd5A6q+{IY6wWGr$mmX12F zed|Pbk+GHb#IwOF+m>Riw*}tM@?;=iZFVek6BCE6w4yk8tRtM&M}WQ<$*rqZagl#` zrCszjmD?jKd-J|TA22p>q2)+3(-bi4wE!`n795n?_$kC937(o|)^YLP=dO4v8D1Tc z?FT4uQ{cq_daJxIyss)(9)q4ziG?KW*8g$30xj;>9q;}}xg#E#r>2?#e11OVGBLiv z>yYTo!n#ig>7kF)2`M>iT?Q{&Hr0{H(s|-Mhl^38vBZOyteJ$BezfS`);RI>X|`D~ zKMfQqeBZNzQ(RZF&U;7G5Gny<)1WZ=BSsd>*a1vgI1XOd5SjrmDtga7tUC8mM3lb) z2oZv3pm3N9b_Ctb>|df8v;D6O-lukDJ>FHWCZ?u%YP1_@RiS|Ft^4;*B-9Qr$#KXhI7oOFX6I?IUxxmUPAO3~De1gzg9A?F7%A}c*QDB?U zY2e4E$~6w@GbwW%(2QcpUbLctXTg;vv!Ilg!TuItE>#+6n>E*5kF<>j1k>8nZ8_?9 zUxc0W#!J72z~Zl&1Jeb(G}tpFFq46m(WXtpxIM%FAXu2JTghST3XF~K4(Mwu+$=gbM9;R{iYDmN9{DlrwxagAU^ zT;ipos?G{TS%|ai24((5T&dBS4!l3GF`w|JpkqR5si|gw47JfsgqJ3QQ8Lh%99e(4 zH>JyeNe1V;JFhCB?n4}xmE(XMKbe9|5-QUF%{TBVkn)~olcl!&KCJS5STf5cd> zPZYq#xHOWL>a^20saP%IwY_kg<2-3?LI`C6@djUI@G@lv|0+fjc>tn-U%e{*MgB-v zy9#RJu}j0(A)e^L`#Mf!#OjJ)t=C5?wIQc_7-5Gf0BKlWH}Q#)FWX-_gfdk}B8Gca z#>M#hwlW}Ut8>Y)Vx(WynQ}oo`&$e7G?I}K;&H&Vr8X`67~*e1L@~A*scp^He`Q#l zC!AHOhooEMPHSGwPqItMDj+X&C^N@8m{%8rz)BIi;1+o#k?f-j$71gIf zu#7Fjb2VOed6F3EOt@!4Q4`X9e9=+zaD|;QC%}S|^12={_`bRzu{JQ%Is|RKt$^J~ zZGx6Pn~xvQy4(=`Q5NuplRnA;rn4^U3joY;Q|%H`QY*0zJ^kP$+hp&J*bN#?mJH$p z)`(Fbi^dBgRhvEgV))FHgMW|K>EQKGnqxJdQKhD@IGlE*x})o$V%FK&in+9zrhA8) z-~nb*{l8Y5dy)TkC*p8jOwc+pbD0J)RvC#H+GXQ)&+vfo&Ifg=hE#fCE!gQNE0Tob zhoK>)WT);=3cM8+3vPry$ZtWyQvb!*+i!01m$E)%@`{oO#+s*V@MVuA0H0C$#6 z?NJNHlZPryaI<;`nhoa?;0}c49f2S)ai+~o>mG+!ZR={Cqa{G%ZsWF&d9b^`qRXwJ z#)Qtwdvd@xY2UN8;-o{22{BKOtdLW~1c}II{7kCpvCx*T-J{(b-{(xT*2py%vw;U8 zxv)6_&Q z2<_=fKUndJ)Iv`hbP7A#N!O^$^*-z7(@+f*yzah>ecLL|b9dh)TPnnpRRAc7?6t); z7h_GBkw=#tlCS)oR;_2rfIuji%S`?;&vStaEGCby8U$h-j@NF0&xh zFIigzERI!6 zaLC}{<*iZa&w-#|d54Q3d+C2 z*9p8&;Dg%3Mk{R#G~OKx0sIzd)%^gKx?=BK^q?pzHzBykl7tPO%}MX7%O#;R9(pinrhRMxmmy_OKjPqb5z1IF(mr9 zj_e=Os9TC}GM+ZnN$S(<8asRJ2(z@>^krl=e>rj0KVI`BhGqFXhjFz^dzyT;!dZg; zO}bsuiK)pWeIL(iGS{zqKZ{!Q20LhB^i&169x)ZDEbm;okcc4qzh|e~o}8e;;y`S7Gy~3RwXcA{WPX0$tR?tXp16XF*UJ;t2>V7v^_Bu&1uXZxH;|F@2X3aJsrc?YA6~IzRss?-2`_ zoG}8$Zvhj2OaZSDl$MYvM?L_xpri3TMy|M4kL`ec*d{l-O4}xbacz?|&-K+=VJ`Ml zdtXVucYn_}%Pcde1+$v7#c|ijsJ-0+pFm+2jWU+KlSefBE~?f)(riT_FZ0LS>i$$w zx9MdxVP)Ggu8P`6Og}0Zi%k5gxe*p-;ZPw(UA|erQ{nyYay(A+QJ%d*K%^pl)-&}F z%8dx;+*wU2#De?r{hO7Xg(XT95z0TL(Ju(6-_}H_oT2spF+KaW#HV}Yrt3*LCId=dE5LkFpivLWg%e2tJiNFkL9(3z^r2m4>~MQ%>zCU~D~ zf*HjFz!)HN+WV(xexJ%)QhL0RpL+9Ue~oIzAWQn;7a4IHoeGp$irOUSgOW6!>w+|{ zbIg?)uhceujfM~@Y7^Gle481n^1$ZkiI!)6rA~uV+Y_$)gVxgKve70!H?k}?AMdSA zYI8@De;!xAU|J{9lDVYcg6P`QTV{qlQ>x)(E!XuU&2U6nWb&^vF}wVM_B>{Oc1`Tc z@Qg0DsCn!g-LJ>^_dj40cc}{=fs+R-_VY+-Z*5c!NRxh;5*Kyioo`8QKhXe#!?sL6 zO(#3)k*BuL2-V(8$Sin)wb9Uji!Hn@!K@<3N~bw(#ig%5klOl8h@1bW<28*6p3!l3 z0=8><#OZk}qa3n`K{b)k!Yw*aKFn@{#Tq09!Uv6eh43X0B;EoKi@M5Qf*AD>bB`^B zkGbX~ehnKa^gxk*K#uqBZ@t&2&4Tv`_|t*!TqO{k?g0V7Cib{}?1iRN3?L((olO56 zPK(l$U5NG^spM_SU6mGqetMKDSatmcA1Gi}Cs58pT8))hnAND9> ztKTyLjpt##j={OFSLJ$=Omq~@#AN#m_^fvTLRr5`&pOIGsgzrXaJ%gz~ ziFE`VPwci-@&;Que1S`XlBes4vWcClojjrxq{KyRSGMTkBd(9)Il> zS_XGz=#zVzT|9U3faZsTFS(bU zT3WWd`rdBf6)ug^JLM{VpGB9h986NVa3)ace*y#E{R}iRTaN;#2Yqsl0J-UK{MdYX z=e^eFm1f-mHH>YG2Fcmr3uY_gV0EBP!oK0N-+sdUHeL%E9afi+{7Hax=(A0Ag?MVu z&D?W!QtZeF0vyR?El)(HQG9wZO=`o>FX6O(zm-r>&$o{Cra;At*WO}`TS=`t%(E8G z^`a|x?~6dp-;18{OLnSfn4k2>FC3m(7h1&>cgBA%f1y5kkak(@I3(Q*(^4gQgRKZ? zOh#U;yOj=YO+nKzTr$Rb^7b(*HTn0e3fdkxM3nFpkZ#_xB%>rBRLgKK33l+z&?+^(ek_d5e%xMwj(~_wgv#8I!CR^Z1PQhn5^wg;|u1JkaGj{->l%K=ta3kzYIvZdTe1(epgHG5Cr~;Kh?3}-!VIpL>B_fK(r40Ek zr>MK2P;6xe7i3eRMi0rP`17Wv++=c`b@p!V#PXzl(=p=KgpgS~1t6Zo&bB$|CJF2+5qBT8UQDV}oGO*OJ@tjtZ*GN%mK zjz0S|<>0UmIoyoOM&S?^HYzKwCnObC<^Qhv0!$x8GJ?;oHYO_YCQG~EMCD)urdyF* zgMc{^s0%RKfV%X^h1fiWbN;DZR6LtZw(fXlh_()KP59)U6D-J5l(Jl#GDtqtYpEqg$#_NR;OLhQJ3iK;SQZ7h)eB zn-?Ev$)s?N*LMar5b=eA=?D7m6 ztU?@z=L5cfSN~K7*8W-nz(4pgEO;knAPp`S(B}DqPFonwVC$Y9iDB~`=3fbAUAirF zH)f;>+cPW}v$yb};f{QktY*56PF0+iP@ckfRK)w5a}#D3mdjCq{pgaQA;}Jt`H9ni zqp!aLindRPp?g~WlAJ330gjw};WsBZ`);$})5P>S2sSxGySOs&X1Y3rO5hVq}2No<9 zl3bH}y26frw^Yc_%v^q*!lkPrmr>%1^?zvHP*)g_ficX?>u}}@AOqY3x{3b&e!PbY zkUT=?frI{+v9zLC4n0ZlA)>qWNml+=kAx#UjYSlFg|??*#cah6@@ByFNxHS2sw@!PR{|k2@}O z(6C{Yhr)etn(v3kxa@Fh1{m0W|AlIxYPwgd;1p7(`^2NNdW`J~mr08wVT$lN7URC< zq_G|N-NAo$O<^iB(NF0OvlEq~!pH1agDLsk+-IQ{s1G<*3wO6i3I7jOZvho$_l131 zfP|nT-6)+(Hv)n*0z-GhkkTMs(gIS_A>G{wNJw`xlyui1{hrb1|Gn=wYZ+_7g1P6u z&)NIzeeK_6CvQ!}{J)C<2k=G=K*Yk}F4IQ)8Ppq@7DAsdEwh z(dgX#Tzjv2yQokTX$&QF{Zj@V^!Ph^4dc|DQp>P(Oxz}o4ZT@>jt9Foi|mECeXWZU z1z)_`?&*uYs=bc6?b+2n3S!_;bH7%H)H12cvMJTQwaS2rf==p^e%gR;yJ;DrqYTy7 zxeZmtS>F(oJues}{%RFWc$G7?l8*@9X(7TN1RRf$+5#0NQBkFSrDjD6%G990@Y4CT zU=ZQm-kHKgg_SV>x&ng5-s#vq+(P>$S% zOqvyaw!cL2<@RjBmX36MR}?J3)lw6b9EPT}7@U=|qO9x6{6ZoXVlyKivX?otGQT;P zc`Y@)&MDo^f+}TOoSQjbDUqC+B!<^m#oL(c)-XS+IFPW6WQeH@y!`Gq-c!w-pdJ)a z)b4uG=6IyJ)Qf)*^2*q1X3U~PGlWtXRU{@JTbf#r5ZTC#%}F<_@IHxDX>G~f38aiy8vmZL zmr!ga-8#~$n^aog_^_j_VSB7Lz9CMtp-U`(p3pOg}xd>~^^KMupnqG~AEFx;#7+$NBv6*+SYj2gU0n-qs*1 z#__uN=L^bLCr!fx6yy!=Q%m*(a--7nI7jtm{{IY13J+}G0b<%kj-(sSlH8s7TnN6(FvXM_N$UtSN9{s3cdxR;ASrY*ft)t)Ar^Mh`x4F(IgN2rlUDh;4RMF&XS;ilZ<3P35|s5x7s+kCLn4+qR^-QKCXwdFn!t=D?U9O9Ie&nCXPjzVp&pE~5!RUsDM;`q6% zI8sSV@_{n=bRc0VONW3AxCQ(wR9Zc6P66N+(M$l{<;e&VojRD|)$Dl_4QYMjHQ(l6Z5bAZ{j8WP2Si0NuUNR01yZWI^4OdlcAJU2t%3x zbdnLE&VJGHovv0e!8Y%4Kr_4|xaIA21+=}+a^Ud-$nK0m{#_&v8;-eoL(6TG@^h2L zD|~ZW2Qp4nd&#%JQP{f(WKxVC}3lNB7VbafK> zbn_@LyS6i4A(y_C%vkq=` z&3>*@HTokcsBq(L3u4ob4fbHU0_p@{!G_RspyM*qe&85sSKC|%#xTw_5M^BWbx}hE zM80~%j3V>KF}>Dq);u>KXaQifJzXmIAIFNCe-bPpEX+LZ#M9jbIJz+ej>0+l-;mgn zg6h@-PIYy4gs?@WNNsuG(a`)3h|ncX$5PH+tB6tQ4otrOM&ARjyCDk!HQA{hytj8KtD8VqUtSSBtQpH4S~0nzHy+uT~Jnp(Tr zbu|ATx_bVSR+;|6&_=Y{Oyy^u1GLWC$AfW&hiYdYr(SNZYPycr*n6a**b`DILqtU* z*xeRCUc*3@LYPC(%Z!Z8Bn-%WYKFEmAOj$FXkXD^MVK#gASR|&?M!uP+;b>A;ds^Q%WIa#Ye01xM+~Kl@T`LcH)&P7kOL}@4dwL* z0T-92a}OVjCp%RLHs>c2x_6`(yWKRfoceRk>YCnBh+|!EJ+g7oZxV<(oqJyihCM@gGuPTlzn&#JZX5h|fr`M-{aT+Yz1 z#Q<6Xo12}Jtc5Tr8Z>{SFFi=gAn7)dBcJL{fx<11UGY zLj>D@W`=tUqvt*-q_%DG(X`=n!fOt+Ew!L&_77g;OW!vHHDB7m$!qyLoC*1dlqqQX ziy{oG=}BT9^|~9CAHqUqtkuaL@f#em zGIg9h`ilVZmg;EdxVxNc4Z)c54+?5LFr2J#26Lq)1)D#EvClO?cW-5OYnp7gm=bV} zWSgP?<%z-*h=Fe4$~_KlVp9~N4ZBmf8UegAEO1YF7h2Tt=5JMdR*28hg7*783+KoiF$}7pcotsS~EuM@tO4FBP<)O=gSQx zoPTirPUaIFgQp~dn4s<2u%5a|URDk%)jbbLeLT2d>*i=-HXCE=mi5pO;`tZcgwQh4 zvSfILa@xS=i$ByLTKYCn8?FV1m|6&DR|H8RFlhiFd^y1jBSfgT82-^h6f49>@e&+F zf%#58U^B&CxmIDbMerZLcif*>$J24i0L;(OBL4=!VO#LL+G7^0YHe`X%8Ys`(a>#I zRvT68HU!2=IRLH+0h>sQau1gXC;W~WEKQop%G&_sWUM^r1DN$(`v;JsjRD_+Q$T`W zZ&7h-1f%6+h8uOi2DkltlDX} zGfnfTaZjz$p1x92bQQ9HS3VTzfxP#6cB6|_)XAv~tCnP{%#a&|vF(}4Tqun*IXKU+ zd$P!+32KAHtN%uwe)mKtI*sfeWDM+N^WXmZjndluziZXZP`bOZdu||&#Kp57eARSi-DPs|`_Z0UfAw5DQaN3|{ zb9vr+SIlF-zKbJMCNy~mpR-{Wwncby*34Pkdl?BS&)DDf>E5o0JOXIC>7D9|C7)(b zkR_HP@Z{&72a#0Lahss|ZC85(z@&8tD;ZJ}8RyF&h>io7MG3t9@-hU68G#{07*Fmr z?39dw9^10q3Tgl}OhDYdANQl{N-&MXPH}C+k9X=7huvfHLdC%0q9MjdB$DCa%3!zo zuhYe9#nh1C32rYBK3ppZ=t;rKr#`O>zTqzhPefX`2Bym94L-MR`c>jJ@JrCjqXL%D zaKip%p0NNQM3_EzT}T@WYZlB{d;)Kn*@@@Mi#tED7QzlXKKPe)DewZxRM+4z0y*9k zRpqcXDuT5iIDMaj51n2>U?H3-+0c|>-BJ0ZcWkQlf1mA8iysbL!mLoo9v~WAvyKh# ze*tN5nj4sE*HX~eW!yaiA!-xgsca!^m>lrry?RaKM2`!gMrAkn`50kzn?(05fV7A* z42kM5LlJjCgb6`E(E-%^kEkmWIWN;1jfTCzmliiD%TX-Xd&F2DIIGY{Vp2!p&DG{5`J?}44 zIAvfz8qUv6-Ja}vzz+J{5qzO^4k}uoNrYX&@g2@iz5!f%MLmwGwHmjA`l6w0PYxq2nReFw&&|B(Xl^)Am|2cn$U`2 zc7U4OC`EH2uYy{>VGQv{D-?T9N?HO4=U`HkbbC0%4}Iy=-OYLZo_7fFu)_-{=286c z0Y!I>gNJ9Pd2&*am!eFX##T6NR2tx6vOQILAUFpn^k-%nIOjZYH%`;$ z)3N(S6~CDsBlqzjeh+$X+=g{^J-Z>`ut6I8{_d|J@8&|Q=(Yup!MOmc7Q3Mlm@2gU4D3~Pot~;M zI(52%Bp3)|As;sHP`Ll8@o=|$V(!%L7o`2S>2?)NogOT<{_uGLeOPTaLW$Q@?xzVX z=$&lWLBL_EGO98!w~2M?p{AT)#A@5X1b{S_SLSm6uhPhTq6IjR{ecJtAnH_HpS=sargUkSOlWO#^# zF4QcTn$L@?m$80s#+{7rX;+7TQ;VGf6&(##ZO2%I^Elpa&yWZ?2%sn8?v<=j0Ur>S zd3zchUw}K+A}?6%^B(GD-7Q0!5Ms?aC}fgRI7&#%unh}J<^a(6duNGd-G*6I$u^R3sNW-SS=XvZkmrZea9d>H;-+*V^QJ=Aq z$?zQLYbEfq%ll3$38g5u0*=VKIPe5BSjlyxU^gLW@b4SYP zpVIY8(d1xaL5KY=r3KyRI}lI3dk3heGWzsZI~i{1o5=704SSO9CuP9lgdE`9xyFM5baDWj1d zj_k3tq$C~t?q3I_zT=7^zh=C5)d#5M=iA`)Vuxecn-rkvv_HQUP9Nc64M5NHA`j&Q z01^p+0~B^jZ}ihppud!(ZMDqHhJGPDN=HcHiB$^ncDv;oLqyB^is z{1?EXVL995CbgnlKl0z4$C5B))?4;-ED2y}?r6{GF>FCiJ=G)k5vGqM{MBW$lJhS5 zov{x}dae+Oxpx7=7!9dlN^K`2`Vj{qm$eqTo*@ng8q@=_fRIsK6?GZJ8lx31>6g|* z$bSvZVytnx-V3d+s#rw<(#I1Mz8%j{zaWC`c}V=D>ppGKS;P{k)yu{v|Jk)tr_T_TK_r#2s>w{BB^%LNP&MP zYww32K=tzbS{QRsq0JC3Ebkhi96S&YyfCB+h$%l7A{#)Ik5y$}19g~fPm3B-=(Rq2 zpUvJ`b-~EZBsEEU;Wk%m{^<*i(=KowSjNTM3wniQ!{3AK>DtK_OTHp z_c*A0x0%JfC%24VXfl(r6m-YwI5;MVkkkwRnrUZwRK_V&UHpgtQAiL4jL2J8S(FqP z8BrT$_-@OtAfgzZUQyXgaJDmbF7B>23>57hL^uP!m_V6K=P;uhn(&2$&*_DRmC1aa z-S$XUw;*2OY*edkcmM$qf$W7Blt!yKSJMko>FejV`%2=L%%~QY92><-mjK^E5m9Wo zDt!U>W=g<`+u%KH4o77(kq!8SiJoFRIKU@M`bqLw`92hM~| zP-0^A>940A%uE3R<%mvBXLmQ=W#u<7*RAjIP0&6416>^iiD{A)0B{&jp5pyofxs0m2;Hr=qSG!%mEHj28W6%C04R6BiS7Lcp#ul( z-AH$3WR;7YvTX#s61p>+6T+t_ASlQ^{)G|b1EZNecm$vT?F_TrsMo$fv(8-pCOI#yN~yY_$s#* zPKThpX5)HL0^ra|%-#5;k*QJk@0$wQjl2qglp}=MEx@zEM%jt+vC=fz#MRkOZZI|t z>ETCEQgp5L_p5f=)37NtLTp@Cv1#Jrgjlve zVqs$&t8Sz#v>>BUIm*=U@LlW!*lMg9Y*Bitg$76T;(LEV_aN55@?6Z#%{7`_%DL#h zaCLyKOfm5K@!A5#Zl*WQlga)!k_EQzi-_8#=Zy0axar-=c}FEPJG5rrx@n8c7=Y{ z6|kRek1He$Z2$^Bn5>4Ed+hN#{iPx0VIn-&5d&XY0!6dkFC+Og!N%au_I;=w1$f#e zbIlrpF|d8CRazln+Qj@KdJ&|t9KegjMw#DzF^*AvC7eDEqEvseo`hZ{jBv{VNy8H2 zVsao-zxs6g+6$U~8%K6{4i;o0N=VfoL7u~jt4GfyL2T!b7-6xg>IU~rCll8R_r*2X zlm9Z*?%i7wep>b`7n~ThpG_$u%8aV9CZP7R-M2C56lyh>tZi~N-GaDmg%ptBiGi#Q zk`c>^*fbDjQfqA$Iqv00c>6t2&`T|;mUF$)pPwBhE*h;14{-uVWqL--gAQakX%m<> znFWG!E+y{9Yw*b1LoDgh3nP>fO-hF)%-_w`QxtzUNZ_+22x=Wtz#Q|iw`G=I-7yiA zG~BYt3%d-N07vH-0#Zfv`JnQrL%_1I>BER+8mKM)HzDSIgBMp*z4?$I@rJ!chj=!U zvj7$G#bZ@`=rad!oPcf)XCQ10Lok}Bn;@*N!1)zXLwBjXn#>SI0jVep;DX8XABhFG zKXF4y)s?6=v80-(Dm0-o1@q-H343ncam zpGQGT5ssPY*Mnt0hQYv2DPru6?Oo+mo2My1m^26g3I>;{W_M>`mEMep&_;rh9yK!t z9w0mdnpUMussHxJcc?-o69g~6)fQ}AMrR$P$0jm|SMDP|j+zyRErDjs-M)f^Q-3Qqw#vr)l2fEaN=Z3Y3|x*UxLB$`DC>Cbu4 zS$lylJ9JV8)P}$i;;D%*u*%fJNdXJXt(23`DJ>L_#pxVj9(9i50X;X2b=r+wpVaBr zsRWzPHV~&fP0%BN(}O8|?zIYEz(ZVBro!o}p8i9F0tg4+19R09k-&x0>eAjj7c4j@ zIDBs0{%LhTx3;04rti~mBmqw#te+H@wPI>v_UW4Oa$tKq>xKH#GN7b_nino>aAYKl z0#6=5v@$mxf*v34ABnD8LM^U(nJsB-E-fY`5AXX!0qL-u}TuLoJ);_B=`2gYkwpO$hmtu@pt&j!oN)$ z!!>4i&Ov9m@w`Xy?wyBV(L`J`%vD#@u=G391ppRr%rByA)>zsNb$a5>>_A&-5HP}2 ziQA#Kl3t$olyx%hQT<06*u_u$bwxrj5kt~(sBPcYdLyu+xz#|#e4g_mapt~tWhrbA z$9n1kllq^WNlVQFVrSx-@NbR;-&f(*zgoH{e|_U{&YY)U)_pip;FS?!;pJn1rt%%+ ztHMaAH=5(K4(YoBch@K2xPfpNwkh0IuUkPV*(nIGR=vzChJS$m0=hqM7&tcG4&Q=g zg=G8&2FQqQw z?WQu;&u#_l5J}`s0NpOzIjHIZVK=1oapr%e@7;Q-2G z4I%U>S?tzX6%}PehszxkfUaqiV^lOc3aFP`^l{4KxFr|BVB&*iT^DhWJ2Y((>{s<6 zoK(gWO1Gd6q~fxMgf6Dex-G2k&V^oBWj4|dBu%*f<-;Sl*VbhopuD-#&WG4=@1>im z&j0|>F$%6`{B3kp;_7}@!N-B}sZy9TmV0`(Q_;0pzN`G3;u-akkrd_9KrUvPa9oQQ z3!;uE`!P3(Dy6Tk5mh^7c4(GitVq#luMe`-F4jCe2)JV!)Edv>iqVE>!x8Uwi2o!E zs#-0y+fuX^1v#F^L+89cNxO3>2D%GSZ!k6)+CdZUh(qICNc4; z9Cd1Zi2W)cc(toOmp&%P2h0~{eOhWK*ZT`rlmEJ3j1(zG?LmvP=)c{}t5+wZvozVZ z>P$5iam<1LriZe$xTK}UH|^f;nl^jg-M$Mv2ovZ+2vLw+rH-$9;N?^2K*}$)wDDJS z{3C;$rn~g>A?w8FLnOrK@?8C8|AGW$tucK^;MQ#XNNuhs}fUiU+BY8il<{shKZr<%vQO&ML>BE<23^wI`qKija4mul`_A z$&(f4(!8>O^|Et~-R1ywe{AtQC#Kj)srf9IA;+nYq?GlzYDXGDW-5v`C7pwVX8j=Iq3+zs5leLUbZ z-O4}nF*xfxI3w;ro|#B(HZ7xQy+246a(IvhY@(7ZUImdjCFG|nj`VWP%*?oe@O}i! z`g*`Z<=s^W8uNS*xVujf6Cn8+>Aj3uHC)ed!go?Qpty*kMSxc5Vqfljiqt^?{2#Mt zx_dagXFg#+ghX+Z)z)Ti)3@gTPX-}8};9-bC{ zT>h59>YBzhvS<0t<6>VwgV!$IS#fY43!Y8eaVwhlm!Ytr-Z2B&U$uQ0FqY+JWGQJ$ z;Zo-C;c8j>?b!ppX|b=U(`rFTY=X@lsf^X?Dd^L$QvC!aQhnInt1P{ir&w%$T#C3? zjhu1sf7wQzc2Vu`-mw|@DY3}ZT`;hG==ZYv9h9AKU!33B)y7?lq)hXE5^=xR5?Yv- zjh_H%oaZllmu(RwR{GpiL*Ba$^mH~xO3JnG z-m(J)1NVudj+eq?8uuH}LA$Fu*B|oko|u@}tLO)uAfceF$Q{r19&ncCG;H_sdK=eD z@SuhN$J)$Vx7diw9nB^SL%?d&zHcizI*Nl_NAXMd4C%(e9E*rKZE=y@IK3a@)pp-| zzjJ(k@8sX~*^;Rs^SNR{#sd5Ya(})1BV^szq)LmLRnm2VA31G4^EAUW4quS6zx_gu z&kCpOztAHE4_U75z&`D20-Til1@PE}HZ3S%aF5Fr5NIQluP!P7hzWspKT)U?2X9%d z;7MUHHx~cPYN+ZHuMNf!Rvf-0Tn_WGWTwIUcp!k-3Bmn8dX0q0rINMK$BMG9nzyb)hdlM+fQ;o5c-h zvpR-(`CZQ~)%>?Ab5<*{z8U-pBYNig>Ku9Eqs%)oF*qxvEML7q@1-L!WAX@^vu=FC z!^7v*^dslXH1W{uZel}uU^Ot~0louqFWsd)^7J|;lhpFoqe_p1wBwFHO)bH=yII|pTm{S#O%!2!47-z(o7qfX3;)VVG#VM$!e7v1iN z)P-L~XpoR4<7Ap-BV690ZLbFRsZK+6kv89nRG7JA_|AAsEnBERK^jb7K6;iBoa9fT zhVJcc?X6$xmpvGDFYcoyJ81UOo(f$1qxq}XtVWiW{z$8b=P{5lHE8+E74yAhvOBkF zXa3|s+cjX&lf%;0#BpDAtbI)SZ0nKHyuc7?FcrzmG!Ha9F;$-PlJ@JVoaHlTT!UZh zwq-OKk~F(s{g1WmU6FaI5+tb(4JL9+4QEel%Wc(F>Ld$(Q*Vx+n1$83vA^N%ayjXZ zXT7Y*SJN4Vi!s=@zG3x*alhdBiqq2`?X2_DzEZH`cmmGpsyLEu$QvQJiWbI8MUt-; z5MP2Hkk8O=6TDS-l;EuCaH9GBu}k^&>IUoC-h~=`1KEg2mZf?#_$HE%;N=im*y;FT z)qC~U!Jp8vb%R?QhO##E9QtZhH&WKexw>!A2(+cS?>7=l>}d6SpvB}4bYxB}pCrtd zbG;GQ@*DBv&6rlC|0ol>9?m-$)!!l%kgFTcy*?l0gJ;lhEE<)n-ezyT%P+AN&mWY_ zylW&M`K9_1TdqJ*Vj4>~{nhN68=oT|VFxo=K%2H9v27nI&pIWgVpg(OXu+k}fD0%Z z?q;kow_bmAf1iDM1L_0-d!>o6tIy8PP6u5|?%!WT%MMzuGC@7Wgp~8)8CWRGz&q#7 zUIgBYpC!_6;3uqr#(7~Smx7pBHe3S$7`CRm-2W~u#nvO8$Dr;pbP8oq$z5hjocASP zzo~kXb{qYq)*~`-;8La8<}tobTe2f%k`LysCtd8? zjL%Zm#8=cl8sf=GA7^eM^9v*HF`18sjokqu9OZmIp*8MJ+}W~t*FP&)9l zeE|MQLhs@FyubrfT>;cFJjNM<)AVFUMs-#a>=OL250T6mTzD#k8(K2JY8ul`VbB7a zMibF~aXup(0SVfDm*94z0A0+A;5lDTk;E6Uc(AUTeNp|fSDh6#Yp@kHcD&7!)Dk&+y_YMqj3a!l)xq0EovgS(eQK zwq!5Rrv3|PpU0ch7jDNQz19t8X*A0$rJsAwef_-1Nu$3NXH+OHq7p~O$7v%0s}Xr& zS=(UQSQ&UyY<@9^7UGCBqx0JYcd3*rQSIo4`W5?UiBHkpuUy-GNa1}^GqaPNsnZ;W!$*=DWR`&4Jx^yM76+ zZG>`i-AkHj?fpE1AJ_HPu~+Tdp2;p3@q~3BSn&VJKckk1o(g#Jr+#a;U$DRBBQWfL z{YYhiFo;N(kty$YXY>Y*!DuvR=U_*&IkxTF(!+q1vym(p=41Y^R|#>(4^3c>FLf?E z8{QP(;M=Yl$=Jhh{CFl`lZB)rov_}*YRh4s60tprekgZ5*o8$q6_R(aJ0lk~<@K+r z2&SuDMo9(1@&_A0;gPB|z}Qm)zY=C(M%&nGr0O>=4# z?Wdwh>W4oAzI;i(C+~~GtfbPlEyB#!za;ZXG1;|PE#7Qk$LZxx>E554!qTW}r{a9e zjzzW30zIWEIi3qH*EM|^qMG5PpQ>G+^;&;#tvd^~>58sS%ks84-HqfF zAZ?FAAtv>-tNm{9<4k_F?ZiE>md%R?Sd9g=y)0?VuJGktYiI3s>91@F8n${xePfD& zEXE!hGtBXjOOO}y;WRsBQl|f+%!Mf;`JD9UpU5IE_tDqKQNQrnSV<<&71ciSzOnx1 zZ})J9Umn+_g{@md^T}hzpY20DUqafr!n%ihOgx_}MhPB12y3*$H(+P4BEn}ak2H+I z2=lt2o9Ct_$$wWUCu~uBAK&%h5k}}^E*uge_P1SD@`ml-AAU(C_6&bMd-iiUX_`^7 zfdB}T#u9$rlb0zhJKXWt9uyVXX~B10L zxu%8&Awc37M?z!({_Jl^2kkhn0qOtcq&=z6&;tCJ+sL2drFjdpKGOZMeZA{@sJ?pJ zPL5{(>9Nbh&+@h}shBYHWmg7RbKUK49?w`wBzn@mz#OYvy{G!LPsj_aZ=*HndY7?I zAelx@h+&%DQn3ly|Gn`m_Wf{=2JOg>#$Ak#N41~AZP9zy&Nw(NXJ+@G z10|?%v;U62qCV+K0vio6@%z?86)ingVLE0U0#nRD*V;4K1xHlb%r_;rx2f-22is)$ z^xmAB@O@FvN$F-et1HJh^nG9cU6=d0Uw!`%%Ki&mh>HP6K23O-2dN?o`6|Bq1%pb) zZz@m@S@$qI=%(YE@hQM>(D8o#K|g*Wwz@gfw4sIA9+3ypNdf}i5%8&99^hL13I2q= z^+V187L<+3Sw}AXW@9-|K;O|1Sf;Ij_B+r)35EiRn8<+0|8BspID&7h!P6DpmQ;R}Jk^fyl z`RqBb$x>JDhF;fAR+K)9Rg)LfPbJlqNFrTLoqP*Ub#z<0$@m_2N2rqr82XKn6)Fabkp>sdJa-)%P~+jT)FC*PW3s z`#bc?+is-RA=Bo;x2oli6DZVrV-+b<9MX>FKG`9!?kQ@D`P+N@Hnr|%Y>)jO*2h0T zmEm{ZP&j3moioxOVas~G-nq7Z7NXWV6VX?c5b}Y5poccUamaebWX$3{{1G@%nYVzxSh??$AQ1>Wy2Qxv~WKmb88+BKj0RBtsRR6?Q%RFD|Y6H#Y*%JBD zYNd;$RC->|txaXAx?dyZ)g+TWGd|M%0g`S(0_RpmKDKZTwLAs%kOU#oRP%A^JvD?2xi8!Y($3?|!)kq`(t(lie1By7iho z&=Fl;9kFSux1qgGOmfc3mRX-n(NEH#c3cWixZLH&*zcpbIEqv*J6tE$~)d7GrB>a zmk_+hf7vD1-n1#K`rkH+*h>Gt+!Ga^+PcqkdkRYvkMd}77;2_soe$~wmB+*{-}{~o5a%mu=FJJZ6;AJ+&YAlB>EZcJO{1Pi*&k|qDrcwocj^lL>cM~N;RU{ zQbrpR`~i_RVIkVudtY9R7$nvr(@(#MnIy_8ttZ#wpelQOf~Q<0knOMBs5SJA%V539 zH9^&c3-e+7!M$juJ!+@Rm9E6{^@VC8uHmK(@dWDX)P!K3F>1qpg65HLG)^7Q`y@?2A2SjsP#RD3+B~ zft1xBV`}O!;_!GC|BW}>3;+KIA^$+cwqE-&k}ow1+wY|uvxJ{0RTTW3&H{p$l-O;0 zk6YTwAGAEJU~5izV@krg`&cM(w^jG$=~kRcGj3VjDyKeyQdO5m&fNC2zp_>nx*chu z4KJqV*acxw@zly{yR3wrfMamu+}nZOw^8)ZF9>XN$thWs?A%vQ#PVSo~O|VU8?StzWp1tQu;P2U+wb;iH`YGWwB`WhbGerUb{mY?~_G zu`~nU{*JvrnHTw?OTk01GF$T1o~LHq{)+?uTOd)*$Q>LMb*uAci_8RD#nJs$X;Z&f zau@BF7%<40N306w>Q-l*DQNnFTtjR1-BZbqCSBrjCEv?Q3)A9b1UZWQfHS$O#78=| z+H~|%&HOp3Eq01GzATKds0{QvTkTYB1=zQucgeXCNS2C3<(xk6IgYGEtcOXNBbTUM zJm9PWeozjEr3H1IY~MYIBk!SJ)%uUX((FPMO`|10 zD$#bh5`E}LuI@FK3}M;7u5DscQ131+HCLoVIT6$~qmHF5dwL@D>$P7q)}E=Vem+%$ zgNUmALaZanZ@t0XlfC8b*kL=`E?wian2DVz0@4DZZmoZHg@1n=m9`1X)P=}~ilgQ< zV03%wBn8bk-Q!2y&+G-9Cv9&&SOD2=e;fRhknd!ZtRnw=*-+8pp>2*yj7dw_gVwEM zd$g#=aBZK7HN$QdX#J#%j@-RDu7e*12l4usNxFKwvO_B|L%2z2kNj{@adrXTm_^Z8 zk*KONh_NlQeC2BDu)E-04}CEc(ScNv_SyZoPeM3}&(cx?W#P3!1?}`QcSEoGb+z5H zc>8oyBWx`*urJ}9;qUe`f98a`k%Ygcwk<cpnnIgw1btQSz+StF-OE~Kp;Uqvy@ql>6~B)_2DYsZ*FgPb-whq2ala*z6x z#<5gsh4S~4xuL(xbq)#ZEvWW67f%YP)zr``_c#;_Vk>OTB0#{KW4^?8@6eVHJh z=)I&<(hm&LH+*Oh4ljI7@H1?wFlhKic`ok%@uR3KHb=Fye)W?r0UL88;VC8qOpPSz z^YPD0dkSU>%wRf66Mi8mdQHhC$(#y{#AHKjnS&B>%aviHdX2g`NjgXwc$^!vh8Z)D zLv6Y>|C~fll~XLHK9#=3n&cnS?}+&PhE{ST9oqfEJ=@|1b4u{hOBBOR$m-#E*o9gJ zFS>eC$3lVby8d?YOV-PaH4_+hclggTQj_6H)D#&Pwdu=U-{6?-hXM9Och9TFm-GfCR2GqUS^?et8uMdRA+9@c8?wtyb z%Pi#Q$*IqAh#w?aQm8NDFs9*Ljn9!aEGdKc%&m;)2U z9DDhV+QMTN2Y)79R6W6Y5eJ{kSTN4?VBYLNSDN+2=L7x?<&Yh=krp5FLkoSn*p`h* z5gX6Ien;CU)E*6=Hd;1pezfCtFNa*MXI$7lEGm3c^E}duPNkXiKDJJ>sR#2%C!I>% zgAiAj!*_g`ldW9sQQyigKlX!i>i>PgF)@Z%xv_nx$k+`r>eywD$(bXbMce8HCLdzh zbDBR{dAi}0C>$a5vt$+(2i&l zDT+bss+`!W+%iosbB@RMJy0mzu6JMc?kJH6&F=L#Tv?8gm(ZBo8QC~w8_q5$Rp9#* zQ|UmFBg8{G6~<3wnntuQ9-iVI--1~yG48e$?y^hv(8;{ZC zrE@JNw-VcvHY2}8o;w)OV>RQ?@vI_-{Z4(qxHdqgd3Hg}_`76{rL9Wcn0>7>RHW$E zjGj7-Bp~D6oPlF*M3?`$ih6eyb|5$hBTJAKG3Od?K3i^Evsw6DDPOkeoTgN3inHEY z`IP9vq}`=4p^2xJe9d=E;-KylGVe)#)55*gO~zIKHtsoITo$Hj3oGqM^1RG0zv;qu zWA8oU)LZUbeSL|s!Eda0_-;xsdS-nR-}c53pQ|j6T0jG<^G{=7CieLPuPNIBX|w&J zJc*CpkmknWpCgyU>ypn4J-S=nHDa)dg!Ai1JNB;~HaPlLojM=&t6Me0>}aXyS?m66 zWd3)m_`lIaq|PIg-0L=*%taH@8xoNe58i z8C@(zR5DB_ILP*3Gzu&S9S88#Pq%oIa5CRKNs7A-X`k16#c3V!o7!r4Iq$96NF~?G z^XCIi?}Ae`|Nu>0{$$yVkrcchl3(JLJ)dex~$nBsHW-4e1I;W6SiB(OQQ zXPZ5vpHMJ{iYJrmmrEVdRkrGurL;r+Y}x-2yZHiYvpiw&k^E3w6(35FAIXSYcvp6i zc&1Hv3Wd)&Zt(~sJ=+)0E|q5*iJz;LpO@oNtF-2I}``jj^75;48u|-cjKH zb8L_AC5h}NZkRd^?TB!=K07+>c*^ z#2BXijhtg8A-iTXxMd@(?e|n_N5V^k0BXh8e@e{pou%a2C2MG?xZdXC4mPC>pJ%tM zbJLs>`eX#n#k{N%H0IA~<|Hs2EE`lExni8O+jZE~3u`)2!H5*ZMBS=e8cB57AB@8P zWR{z3lBgPgmO+SLtl4vhU?em&2$eok(t zez}qYygdB-nB7a(`61#-~7blF%oSCtg6EXivaQ{m)74HHVhFI}pswYBQYXUTL zHzz1?Q1+5S>KLw2>a#K$qt$4GvYylBrb9u2r}OJRQ~Az(8} zU_uOS4?N0W0PI8AR`p7g92QFVa(No^)qU4VVdSnMqM-ZB`Nsb%a(kajr*Pq5(v`&pDtW+MSUG#BRLHg(p*1F8=WY?+Zn;$+%r@4>|(BlViSv;g6fGm zNrSpJFaMF`Bymx4W#y=>cD8r;=l9w#)IQgfS+e@&qnwI05YPt=^p&?a6=RP!;y_iw z=>7lmfI_$l%NVJvsqTS%zh-27%e?=F(4sf$ou(gp+Mg$pP0h$ecl6$U-_lal z<+wEsdE@)S|BRcwHyWC?NTt!MSvJlyS+Y2P%ZiHk8z{L`N`-0PNCL~~s3%Mtd8%r6}1r&!ssN#U<@DFy5A^9Mia zcVT_X`YXvckD9nj-xQ`Q0jj5ziaX$KH}WL#9ubD|y0gA{^QL_P)$g^z?yLUm*yV4J zveRDQ$I?-ATUFRuO!EHV*Kt51V27!qen$fRIPVfBlvOPdgeat3lx_EcGJh!Kqz*m{ zMRa)FBW5>1H>VSb?e@ycKq$^BM3bCxv0ijP5C|g^A_XjtAAm=xwtEdGGaq*ZVR22h zfyzrHaHvRJWuSDQ#1gvp2Bn$e%}Te^5s9QBGws`e^nW*4`H9!P$NuxSjXo}s=yobW z3d5Io_vTZdMiX|n#oT-G+cm1O`KjxS4)#|`nG7)qwDoz64M}iYkdw#`^`uu@$g6b89l1@9(f46# zyx8*E3wZf(hH?(khUze7c^AfNLmQ0dGoGeTbKY-YG+=cr65nm6a9R-pWuBq@eIV2G zC9t)CVNiTsD-OL(6LTUoMXOSnN7+RP}nkP}G5;($~)WT16VwX!Z5=a4mf# zBqTM^=|W{Je>0r-AO_s`AKcto%;iO>YjBZ#}mmMvb3KT?8}2OUksXs2$-EK5{Iyd zT#K0p32R}{=6kxgZt$mv7sZ26$BP_v9-%IS2%2m(pq)^h;Fle?A4aM>y4_&4>LTt> z;u_jt*XydGf1coOC9!CQUWlW4TyBOK1@p@LJ6tjB#dy!YBM|w4IlFECM7?&3_E$VP{Im z<>#jWndUi=jI-gg=xF6zuqSa^iKwX5@hCl;u?GXD+{9kjM{u<|$_WIA1);@B{coRj z$ByJ9w%8i0zCU=7<_jay-ki+5cg4{cG}lNPP!E;a7|~<~^ zUaq0~9O|6m8n5%-avC4jL^8QE^VsF&DaN0KlJZGX7_w4ZSJ;#Q+VsDN@qO_Bq3kQ; zqTIT^1w?8Dl1VeIWa>v0Gs0nDxl%Bn;I z0EInm-ttIDTjzSo{VkhOmyYwa(q4r`12wQ&hANz29oihCk@Ns3@=4tFhT<9`;z2;z}Bm0Bg?`J(+EpgdgiCkgks&~Tl zbAEv<4{D*^xxALuddTev@{q59_k08eVbH~f+*C%u?s^kEELq$N`|tE>>ILoC5j zBOH}HSxr)+&7Nq8z0P^_@`cJ6;V3pz|9T0n$Byb6dE?jpJgSz#hlX@?juASn++BX6 z)=vni1-`I6y@3CqQjv|C%YYOf3bBSUX(3B#pL!{Q{QyQL-$Eph+GxjH~sT`U7F^BYt;Xg|6 zSd2Wfe9O>v1sm*s|29}1{Op_$%1~o0e9nrJ;dG}%2Han7rUCq}V9 zc6Ww?Lc{DbiwAH>nt-qQh+8vzvQxN6>GtiEL;h1a=k@HDfp1;bYLXlO9%xi;A3hYl z{=FvJ#zXtI&^!*{orhlgtYunz+02-afP z7Q-NX^c=S{3$djawFw3!-0*R0+QP3u3hQi_L*4Crdq>;=7}cDKPg+NYj_vMN%`3uw^-$SI9W9E`$llF&qW1T$X1LJ;m|+hq+TDL$Lh!O2s3~Z$vHWsWdt*) zl_IBr4FIu=_d|fjzV5D4-i@HUQ#%-DQcs}!@m`|inaU>#Alh zr|C9QyD0g;+AMi@ny-tpA0vQd070ml-Xi1#B^e}9y1RYaY{p~Lwpa>Q}W zb*q3E9e7R6x~R>2+HCFrtDdIcvGmgvi&DUZhNU0_OHlE7vkv0<1RLe znztaH;G0$I0rf?JogY79A;`nx|b>d zI&m}Mu@yj$$;a<6o%i8G-RpT?@$+c^6X?17T(^TUQZpz?EEg3%#3L{}*jdlgQ8PWZ z08lSkutvF@g8q*B)g0707%|iOvX;7AFd|N(#8zqU_xQGd+==4yU z+K}6s#Pi|fyD#g|3qkg#@Oj$-lsqfB;K&$oS1ImJO)MZpwZq^ukpfPc!NtX8ncLKW zrTh@PNnUHoh58K{bYb@qR(U?PWOUSElO74C%r%vgoB)Qb2ZVd|oz}CI#L4bm$-)F! zJmlWiQkV05!JCUvMuB`{_eB!mv0vD1tr8=XoFTuNB-kKQt;t*?TWMUw%szPSpt@GK zz^JXQO>kc~-bTLZa2J5oH#Loc)%U(KJJr0^&t|xo0~=8pJ%&uPn5#Do3jTmSyXGCR zP|pOl3NivFH}X(YrbwVfx7uAni5QR;T-E8Aa-Mo{k6kXzz#j}joI$f>auNRWWpmq|={oe2~_9V2rb^cD-kY<2zw*;Q} zs8f&(r$#xT$hyC~_h@04pv{*+e3F}W#&!PL#x(XrnAVN+{$6+k6Kx*7xB4IcxDft> z4A&_`zlLVNR9V60WowPjmx+zowN@~JIJueC{H!&e4JfWWo9cp{|zmGpda$6npfeL0FD z9$OZ#l?xJ{E2V>2eZ1|y$Ll-NQ0eYK7`uM>Je>0IJ$3X5?V(@C+G)JURjp^Y9{!Cu z4GV~VN`@z9!~UnziZpc(YjZk@X7=?mI<2E>1)72~)GdYWK#~Dgh@N~PmBdTX#;EN3 zu_5pLeQC4>I45u;DPljo|1lo*wfA-6?#I;43(#)#9Wq3zHeF}?M4OryBdErQ+HeL+ z$uyj)xjZXEy*g>PD`pl;sx?An(!amVMC-}H-eMG*B)!!-^a z4Efw-I(=V1y$XY0`;r1`)#R`9%<;E%C5Q6$G=x8FUkWc3S6v_XA2wgKtIsYh{CMys zx~|rgd!~JS2U{G%sWNS+a0i&)rLB!%N%0VvpTp+WX2P0}h^$vZ#G)3$am=k( zCEG^bLelhUaUf%pH?3B#vOBwuy{Sc7yWJT@?w`9o4?4Z`tRwks9?&#A7nqGeTS>f#Hb|jzh>4}c(A{Kt-@J-@{5J7jT5DS*G9^4{?4U7yyYDs65`~mM1SrN z{mxt6gkrJx-!KI+UT6%6t%%gw%@mY-p&!lG{cZtgZtVQfaXa=3(9qf5&mK3mwod;# zuy~jehEi!>ZzP!Hkg=M#jaN~uH zRl>2oMMSw9I{^Ranka0tD3atnI^SU3*%1hrT?G?Zso+y7*L!5=l;G z@q7E;sn3C;k(5y4b+}QY`(=Y}%``46lla=`HfV}q8VFcKI_@tQwceyKJYXzl+IhU@ z6Gr9t5>Ie677CLwM&Wxb6>-ktGwf7Y|< z2T}=7y_n5RHBCQFJV6kDy=wdWk+RQfo`W#5!5dJO@%EOX`7y(iA4xjiPLz*3X8 zN1OfN6hCVdnk{XXMyB)bO`GCg`{cw5;$BnkUW0k#r4z`WsiMiPmKbzui+Ks;`7f@k zuns=F(Zd{5owh9+XoY{gXxmyh5d7Zsq91~Zox(|~=B4BW$CsE@vhoZj$OEqK3uyF}$&s*^%; zIz}1sMsVR1aE@FL=#(O4bx>v4Lv6FP4#a=$94eyNjhh6ranu-dLZ~jkXd$+sC$rOdd7$W!Fjo$ zB-3^Wsx)z&?8j@R6l&6+m`Tjl7ncg^%32%sn9hld4>FTXZ#5j#u^#EzbXv@Lol>Z- zU!i9AS>SZt=9vdgf8B15JQ^H^@$kr+;d*0(jclTh18QpOFPeXXBi|>ob$zM~Y)04n zurv8g+wLW{HiX8ruinaW?^GLVC{=~0n=V~*%OW(h3hUy$Y*uk@FT>fsDQq$e{~B$8 z1f;~3qBJkQm&m4EzPbrVvVOi$=9kD?)1+C)2m5y_aSfy$r2Rkb{P-zQcVGr99Pah$ z0^v~k>rwXss6x$a75Tsn8fPBKLAk0LkmZ#snH_tT>2&+7e`xL*#s+hcj4Ql= zmXg*%JqHE!J3j5Ic9uK?WzG*`C8okP6wkyMb=Ig{zTc)31Q zT-fW?*IAy6tF>=8-~BJ=!&H#_}IM$ z(A-PV*W1=>PH;Q9Nzbij@d7YiG}ZSme@e*lvcKAcUTue}haU7xi(W6+F4+E|HYl|h zj2~!sA>$Ce2zrq>L($sfZE4cp7eMH-_(qQ5@6!u6f0Y6rOUr#}W*?Lrf=G#gb?C0D zTZyFIM--L$QYz3nd(+zKDmoe_JL;r*6+9}?4(7u0WCz}+ZPxbgj7q8C|c`ql;y7nh()QWumPpj{!2mGm$G zVJry{^E1;Shz!X{SjaB*isd?U#=Km?i^C9I6nMF4?chqf`FKI(=m1FTH6iW_X|-84 z4fWB+s_1Jwwx9Mh(#OwXpTG8d<=l1DDey0AG}bN_SwAU0s=l1f@5qnUoy<1$EHgKv z5=Z|2zQtDyESv!Tf4VO(H6KNKX|3UWo{y4Q1?PsT=mWt9Vk$(t(g}XO9L{1MWw%)d zv#m!Uc?IWR8Z`YUm8NB)q7-v8FV>4~S_wl3H&NF)EkxxgqX!9*dl4;8(Su76A_tw^ z-b8uX>*!{^0xzQ>MXB}MOwwM0ARcLK*~)@{IqHb-LVz=b`4-e%qxS?<%?7^f&2zPc zcs$jb{h>c}d0bHJNKerE70WZAToRlr}sb}yztbwhB1N>$Tk zwjsOpB_7vkS#io&o*UN&1r;w9b>DMqbu9)hjD17XQPt2$Mg))ZPjgZ8X7gR}lS7)L zZ6lWqi_Ag}ZJO7l{_H&^p4UbLA+ufDq1K^fEHU8u;KLJ_p8l(`=mmmh{k89Y^_G;W zrt!SR1}W`9&~#g!Hi)2G(J53_??dnod8$gU#EwZ?KLaBIp(Kvh{KUiMQ>|ypEn5@Y zk5Y9v@0FvX;$>}GdI=|LW?p?iEBg6h3ROY2sL%P{p;5Hzrg}m0$Tsq;_VwUMrE4TK z;t3*XNEW5bVG%r0{ZTL1FGkgfYa!S7Y9aT3EV-xV)1L1oOD79`r>B+FFY74&01qcvj_Wn34=L6YCO1pl)htytsP3gj@?e7i}vwiAHdK&UDd0|eEHqtpz$ga@>dJC3Gq!N<> zW?dc2*hH&N`_ss^QSzWjX-3go>?GsZ*@UMt^Wr*s82Bn0&Pi6W{YFcJ1?6ELW-s!@ zMCz!bRV3Fp>vL}=d^N+*VDwE(@EPW#T=jfPeF^Ky+VwtGuS-w+S8}#GcVhA-vSezz z78oqm2SYh<{^#UsyhLsc^!XkYt6-ITMRaReM62rn|G~FNS;yHGue#h!%L=bitZiHk zbV9Qw-@s3;Ya+gh&`^0UJ*-vA9lH=Iey}V8@?y*szr$Yq=Zg6*B!R~k-2;q!8(Y{U zG?tXoW&I*^hsj3{A1q{q2}0Z3CE+RcukVL37LUSZo(I|4+21rmEJ0!AvYegC7JUo_ z3++|i7Z{&2ypw|J4Frv&q@kDCg$yY2KAhpE!^1&{UN(6Hj7JO+*<|zb-fVlq zw)(E%!ffzcb|F@E4RtmRl#{CU#>U2SM)Bp)CPurtd7z%5p`6#r`34&ArHNV>bR;UV z6)}0xl38h@p0>Rg3fnet(Ur`3)Alj-3CcUdV zPaF@9B3jgqF8BDodz_B}IChJ)=j>OnS|HPz3UG@V4TA6r<8%K#>&-Jlp*d#SrI>ru zP!#a%VDf4zV!%zu=jQJPuXJ5=m3K&Ksw)ksFntGm+#8|CJEkw(74bjt!m1ATd!T;@ z)y%RbY|E6gC9L;OxkG7ddss&DLOmYx{?x>6tYy0le)eJu=ylu1hFsRI|Ipsv2#qz< zK$cHb63{o(%{K|5^z0OBZ9=fGjY>lRJUC3R& z@tlFVx6N__sk;JW>zQ6!2+!$_=^t0Wd*ari!M~N)2jv1My@7k# zFWHL5Fu^%658{Ay+V;GPqMW1n+WW^!4?=gmtv|gC`K#V#&l=9d6(b$5w-99*j|;C^ zZ$v8aUF|eJRn(OKD-c4Do$T=|FKqWvhraP`2SiO?uvgt-BR|v(1N5`8AD#B#P%nqZ3@A=@FSE|D+2kK4P9_UixN3 z>~$B!c!ubHefA(V1jA)rd^6T;RHUf%d=bw~)9EFtkhz|8<8KyCjZQb*Xya-@2*oC6 zPm4bE8CHhNuq=!%vv@ncRqmqThn{<=Md?3fVll6$f&%W)ioM>^HWWq|`t8^qYu6HQ z)yOxNb;-n30#WLVxJ^V`D)&r6>A+|*m}LC{;G94FaHndHuHE=ogbet zrNDfm{Wg-uqA95od(~JIVd}hZvp0UK$KE34^OPtPQ}L9iY~G*YTe$M@DD;kxRllcl z3>3O4?7OpORyXvar$^l04E6{bJ6w~|{dCnE#sy4-S zLSrcwh~^zyrrg|C4VJ;M52X&Ev({2*ioO}nrehMQ?TQsj^07N7cEi2neEHMc-a!Rp zQ14ZXw4#@mp->E`6M=+)_Yo9E;k)u5OzIC52m-q-{>C z>;odc?T9nCtl32J6Ca{Bf&*wDN1zsFCx3Wa(>gt>-PYeaSM??S*FFpnZ4EK{W83zjyg}k7UenQapsoC@4SDh)fpV~!fdj<5EXQRc_EoDl zZ-`)5Z+yGc(fM}H;LJUtcjO%zVazsU+GIKsvx@#=r=V-~6Izj{v7^J+Pop15tvhm` zQ%4hgI@oXDm5Pz`kN{zQ^CNroM*@~FQVDW90qJB<(+GxqX*<`hx;4jH`Klj+a9VP< zGrJJsDPCX9WLY;-+%Yi#HXxdERK=NZ1m^H$tfy&_Y%=Bh5Xcq=1U(7kC$)qd3 zwZ##~5+EfUpr0a>W#feOdhUfX#}AWOfx9h;LvVu7vO6^b(l!GKMGG<^^(<3_HU0i7G&M zP_N3(x`;>Roka)r0W@ZX2bByAQY+`^JTM;XqGAP2Yn|BXI0YAGH>Q*{)E4@3IKD8( zm`kzL3uP0&_tSgKWdhs2FJUYgw9S^<+g^#%RFiQ}oTgg3&*8vQup2A8*M@NQJK)=( zpLJh@c4gkskKmww==RRhyQ0`7)9L1=9#%xxa8~$s_n4!Xf(FkjYGYNeDzA8tRy>Hm zzQ?%f6FEY+R5e$b47RkAa|YrCqTL&z)5GoGe_BQ@<9B~53JsRQf9W~gY@;0V+VBV zty0){zFtM3QX5`e;zNY4$-AR?sN#1_kRwcotXOHW7VuxX7rPRY)1}f$6DsC{^OTY%es=D<<0QcEFxX*$?(Cxtu9ck& zj2qDV{dU?Qhf7D<%p&P?$_I>Sn|b#orOQG!4Ign7PsI=2{1{9`aUJy>t302wV%DG4 z&STAl@E823MTdAl3UMd9SnZwOAn${Krm+6moEnMSH@Z_jY*^Zd|ZFfXHEX4Z1jI!_&mHWX|l2d)w zB>d4n1ykthoTyZ%SKpE&A+#u~Ovm!P!H%{BwrC!_)2#6*NIaZOdptS0iyevPhzV8Y%j?c$yCeb^9@Uzq&7xbYT5T(lE4nmsGT7S@|-EOeT` zXtsxGzsv_wGELE}mwKj&7$Tjeob$B#roskAjiMZw-AW+5cKUQgVN$S1J6{ho%zPx3 z&dzk!#*`nOn1#+K*9P}7h=p@kHHvr`wW$+3Y^Ncif60<8lnEJWh%|x+`&@=YOsCB> zBun|gPHV>ZPl#P!p)BOax$$$O<>RmNI`s5|u~y^#HC?#_4okO6)8F~ODh87P{exk1 zQ#ojvhvex$rF@J*?-w^R!c0*ug3P~i^mC)6Hl`3wiU!#{+as}2hBbL9q^}u%bi0nd z1r;Ln!Vu*xYufz#p!?+vcJxVeaqUqf&6W{*yKiddLq?jeb3&8kezH7CZMVJI>|+qL z8`9*BNl*K^U-y%D@ZuREt|DkiAT0~bR#=e4xyo;XmMmXS48wexcR-Clg{ zt};%{I{Ib-+3$Kdk>}-iFU`(x%{K~k-@EAD%%U6&>|F6B#Bth z2iNmD-%A=f)4L_-87UYeTgY!+4pJHwa3D}rE~>RXvrdz6fJqA4EE%p^JmRPNbxTg= zs7V91jHL%>eC7@B#^$C<^_VIZ-I2m03;R4gv?zcJ#qDhh^h>he92Xo}u+-G3k!&!w zxKb8jm{zfxxgGo`RQ#lVyNp)NNrJ++o0%u3Uo@A2rDB6^6Q+oy*l@Xiu!(U(5uiCt zl}$i;aR|f-WjG$GFrB2kkl7dZ!FQ*FMRaYoP#MmO;tBNay~mFurLk({9@a;RWZ$aY zzM920x0u&om$ds5G4f%~Wk^!4cye!8O9+qdlRWDY*O`ltng}qyNru#7w$0f&?0MN6 z28GF~VDvGpnM;62S0i5UcM&W!RYoumFV!y#_dJ*(yQwD=y5Gh68QbXwO@%MPbhLfk zJrk`60YNMDq9j6d-OH6JehIUM9`qvB6AhLNG_}ZzJb|0)&g9xDWI5txC^7$de-ygy znpE>Jz-@u2Xz=b3^`)t9|BO)Kllb^sbFx>y8G~O-*X~S_TkX||{Z`H&UI?s$bV)3j zH|9%zE831vXV3}5_;{%lztoypH{%MyNQ5fVPVIp5mg(I5e3?j=VF3cYUeZ4OJR!j% zCQb80WcovxW{sxOoKU3|<fV;%aj8VGuQNJ%kCKhcvCsRi z(a+6We_!}CC*l0lEW}enK+Xrn+Ojg^QnA|Yaazt(y8GZ};l*B%l|gNYuQ=tJ!Z|;&sZj^R1;j5Pw3JZMm)88ZSWJheO1FwenNSx4 zWq-8&?nY>EZ+1bDxU61GIlFiYZ`8i(Y-KV?7y;xX0U#e6n0&1|@ONlo=J^H?L7*4W zuq+nmAmOAj^!&8{!V%OjT2C+D1)33jJFGUamE zS;JTwWXd{y8(e&zgAF2mB07jHENHs6&loHXG>@@}=h>gwr1c0_jH;=)n(xCM zosofG>5$#s5YC<|!hG>_-Dy$%dAcJ2FVa93-9L#Oab5EVWr#G36s14-*7(SrCpbum zBuO4c9V4}#+NO7Q;4CW$dhWw!_B)$%!45ZnT}s&xUFQxh25GDm`g2^(_nXkA;?_AS z4>*W)*jcL^eq(hHei{Zo`TZf-HDui{n4rsE`fM$RKpzSp3n2upP4hYpnZO2CPzF77 zGgaag%wVr_QC{e!`*uxDO}y*M*SX+dm+QL3#9!bc>&A<`G-Y?SM0S1n6lK^uAWRYHFGxL#3>$+5&Q2 zK}-47q|itzevQp?@MPp*x`m51oAE(#adFjSQLxz7r6wFw#CG8sufaj!i@6}%2P_<# z>7rZVACy_R^5uR|@isETic5hNQ7r@b6rRL7e)P^uyx!beY!d7OS<^L;riM|}$?ZO; z{uFT4N&47T{}9jiT?EhXtReI8!4Ct#XG+W^nJ;tKwO8|;0Ez~f`&B}kPx-NyzyMW@az{6sCXL zLfF5ysT~RiNjyX&Fo_U8Nx?>g5}X}WD4an0x&=Txnw0r4TEZS~vm)sn=mbpyzLrEH zMxs3$`QftD`<0CRu^qD;9TdgG70zd{z?>Q^FtMkn{539UaMQ~ARuGN-+C6_Oa{SpU z>^?|(NA^flW-**n7?@~sTf!j83lJ{=`jAU1B&i&8NQDD|#75hhsN8-DiSDnyX`81e zT7S4o8Zakz%(}{OgoF{3kYGWSlTZw!BdK_ob1<{7U%#FPlbi-<1>bo``a36|ir<@q zZQKOO(+D;FtNO!!fjhsY_YCNYW2b5=A+%<+p(9X(NSZBvDNxg)Fpk>9`zpvVT zK@XD+10730P>$SG11uO0v^hh1=1X4VLeIFxBGEAPzc$)GPql5lGc7(I1Yf?w-e?Ah zb!3QLYIcSd#87S<++6HcBzKP5oVuWgSbakoCXO`N+RjuUq!{WEG){>Qb7gl47rAdJx<;@s}l=Qk!Ts6Vh}H)5#^ob;xpf*~U5~G8#VsB2hhu43F#swck_-zueAZXEsn7#>=s~n#5W+__pQC7qa^J50Ep9#dW_gh zETKx;?IQmtl>|Q1wu6tf2}MML9le-c_`7%Sh}}_FG?{-dLy3F<6!jt!{&^(7-zn=G zKoL#oJlUtICQ4!R1q_oO!Uj&kfW?`~@=%fTwQEAtvsE46!<{HZXi`$W}HKB@Vvw>KWynwpZxD{Kb3=`C;O{)_0R9#$Faq4 z^5p5$V7N;!yJua73;y}qpLbdY(FAtjR}qReGVuZcor*XZsO(eK&u_BPBEFchxu+u?l*yR!fclx%i)VITC}A6%?oubQhYQYn5R2D{H_rY*Gy7a+848 zy@FkRx*Y(Fy(kUa9dvUEMjgaK%sm8rQv3W%k3MvEiuj#K__fY$9{;r{`&ghA`2@?? z((qvf(9d?9@>j3eB)5M76$Q2imxoJ!_)^?L^k!yfr(ggHdH{n7%ZeIo7mXr61H^P+ z0m8*n@7E>%J}s6BYKMZp{cx&C=g28&mN-O(w53^Kz{6|?`6 zw7#c|C3OH|SnU=*1NOJiiPmcPh5g-r`aUO6h3j~QLBIrq0@s3q@8iQpBePiDLRg;- zQ2rWyCm?tJ0urt2Zkx4de%s~*Ua>x_ zTWt{+_8Z#JZSybnRgh<;CB(!Q1hwsT&=(y?zrSc2i*HApX-aDet`>kZ1_QMyAa;-g zP*q0H@Z9NE$+QC3S}Vk`G%(?Y-dR79`E$JZ=U(vSnUECjipxod5DmrS-z~UN-;+*o z(*pTM@2Xyto3Pq3r^<)!btj5;G(lo^B&u^u9?0FYj$XB2y0Kl-b;gB(x5#953c!sI zou$0dCT~_u(qPh-Qn2G<^90aN{l54N<4+%eN_=F9Etn_-V-<4%4Kv2EfaZJw zJ#br)a~b=71{uuW-ScDp&>$v=W&^O%k<7UNz1fs^g*G2ovjy8P1#x|^DmpV5R&&7W zvZ3H~4won*|K)YjP^LHQ`+?b#TpKi1g$~Z-at{_-=iHf>+kPZ}3oGzVI!a}k5CCXM z{(B64m{b)}_TsbD)JJTJp-Hkrl&)|Io2l=}F^!|jB7FRG+UO-nzAlG;ALYbbZ zgGTa$@#C5AzE7=9Q^dtl=Ic#s<#P`)Hx{t$#+6@W&eJ*m>4iSI_4%=(Dz@Ngq+eS* zF?;#PJx^cRxK$Eh9uS5xe!X&EjcjV#Gt}LNLG08l9B`{5jum~0>hs4QDtli1YEmn! zxWIY@^vqxD@!#okqQF?Lw?IHoIn;eXN@{IXEcxX8rnWfR&;f z6KtoZRSE|FCRh9&s_r_{5dVyoBN(cZ1_BaeO$g#c@Z`w~as+2aSFOL>A~R`AQ**U? z#hKuA%e_J~`D44@&TT*TZdrAu8+5*Td%%a-Z{Xme(-wWGJ+U9o(jgm4kG_c{7QbjslO<5z)_q`asGsUDBF;az zbh(Sx%^9yYmAv=}VRn^2?#?8c_?A{1j|h>~X!bFtZFlqvQ5}b)58(&=h_&6ZwjcN3 zdIxg7n-f?IRTSGb(nHJl?gp|Q50t;_aJ}A>#J8qD(fS4p8P~n6n?PKRxoAlKZPY+2-H5vy0g}SI?r15a+Ns4=G7OZqj^H z-9xT`e5mS7a9J9O&{~s3W@c6A#W8oT(kY|&mgjMCd{+NE!`xjxg=^sO!YPkL(sqoC z%S4$o7*{8Q`>v)Mf2P=ZSS_P#O)IF2Pe$9Kjm}kk{Ho4kaBX*OZwj;3nyjuOpJ7-T zib=t(zjuBq?oftyU7U5|wo+>9P!3Tf!@HPEh#=8j zeEvKV_EPI(hWCT>29F1A@AEl$N;shlJN?62GJaYUt)2CG`=b@jK$sE0hE#ZAu0Yr- zdaSt4*?fdJ|1+7V#68W|6(i-gwp)vOe1Abl{=Te7ED7O`LYo0|zz9v)vH{Bmu>#jx zaq=4pJ%J3zE7GT*G*+Hg(-I8TwxWGTbUeW5aO&fsI@yLFJ{Q;V4U~AfGag+|fH?lo|iXS&tBcTWI<<);i73}-s^0wXHkWo9DGP`?(oWTkLamEW5 zRFtwZD?gP*Rd+@TtFD1k-n9k)0D@X|o|NX0kFO(0A|m)JrVR#eve_MeA}c6mHrqm0 zHQCFDd8{no`tQV)^Ftv(;9T(ejzhxT>9{BJSN(C+CrKKXq7+#vFO>(4T79nZ1&D^= zblG@sK^DR5GWTiaya|r3lTFo_Yih7VJ~7#~#VVezqPTl&l7ziZFkFG<;|sQrn}TH0&Y0njS0+ywPS(hOw^~$4 zyy8$X^|oIx?cDfD&p-y80+NTmtjE%=jUw67s~V5nO-=JUiTrbk-|sTJP0Khbj|V;^ z7kf)Qg;ev^JGeuhJmR-@r@;GAXI|wJxxbrCeGs*MyYc(bhsh`o|DCedhE?uMzBQsU z)T`nW)YjkMjXHCNsmMU6Z6PV#{*JD{YApnF0ppU#z_&9wxYD&*j z9D4Wm*vdA}?XptK@;L#Dd1ssv)NIIxG-1o`sl)|&16*W?OLDh0505Md-<~) z3b>iFt~j*^9Q5X>X)yVdY6n>1WMe63&v5kO8^?@gYh7Dj zQ+Yu?w5LceUm$v#CO9m?RvIy&XBa5T{lPvZ%tgOf<8~`s{==;nb?XmlU#|W8R^I1X zATga4$C){zB2%j&iy*@Bl4jFPYndixJAcyZQ)kd6LaUMbXt6d$){Y?iD4YjI9}rG2O z2rm2@b!=lGlstVQlvh@-kMij`oNR6`UG_%wI{(>Hw=)s_E>o!8sb&0rwtr`uK4G@% zkk_Rh3Qh!1;#RlV#RYGQ9rM;&IAh3D%3Ev@gPji{mC;%$vse{tR`J` z_RfSz8ojXt@-p93_0M`9c80Lc?I{gXZ+7s&OOLKf&#&#b}v|2hT>_OVn)ebhyxA+$%%dWcZPO@ zbmXjfMrOR;?gsG(`(FBXk;O%9C!U-p_O7S&l%I1r@6AhQtfU{p!$d>lM##KyJrd|y zyGg%zP0>|>WVb ziuMr0l=p+y?mrBeNa>*$)3(4J_!)c*mgLB@-NM5Hg&6{f6^afIX(V?s-Sa<^aYYQm zr1%XgA$6$*GVq_UeJqWMQz<4{|FFKM_cL+yg275C?Xlb&opW=uv&sMS7Qnx9_7&Jz zWr+w`gGYVyc-`2-66{<|qpRa|$rb!f+m74OHgd0p#?9b<9_2qyTU@l`60pMSJ5<(3 zQYUJc@*vVx{{|x2M1^ZcY>7xH+5CNCL971KBS(|>bHt4+L}B&;tbzQtyKj$qrs}(; zO_QDo(yIhK)xX+^4^&$fY*$+RhWQ$97#Vij3|_F(S;= z-z>j|>kBc7&C*hVYlymWn+nJ7&bQ0s_ie{l*p8!i6$ds{UU0^%$QRW{k>ETaXGxQgKhwOwZ%+Kh*bsBR=#=VP zGv{JDl8-|C@63RUphvpZ}@T1;O3W#hc(eS zc7{{UWmHBp1te_uUa{;Z>y(QI9H3Z<%kVK{mgjw3kBy_q4|WlsD=@^Bmb`1@T`6KCohb8)iMnqzGOHw8^on7r$^M_G%91qj&+ zSi>tW5ndH_V|$MiH(fG*FS?}v^^36(TzRi4>1=B*$Fi;Qe%a-nGvW4idxiGNEF(YH z&a4(3BQDc(Ja1AM+I1C&C_JcV_3A5ThYE^C53vx@`*BH0R{zeF{wB)V)3pv~GcPFP z*#X{(psm9-gH`Tsqafr(#^dZFey&;oRow(IJy#c=(+Sj2iun&S}&ZT zzV5c)V^*t(;Cj9C`bGaee8xJt;7lC{k#C^6*uCGU|LxGn1AXi2_tJlz-7;lOG}WFC z^hLiNmbV+hnVH-B)Rt0a%o_YeT0ZsZjDoFhTX#$0#v@VkcPECO#vXd?LIsH{PQOPcvB3l=ewi5=8T6jH3g5;;7NLLouf5rrne1XV+gfE znQAt-9JfO4uCPXvFxxWgloc6~U2wET(%)^S^|@)~Tt0J`r(jGTl*h5_fsk z8}23YZAnps3;(1+^vz40GiJptiv*~ce(NC-EiT5#rb(ex_JP??PoBG7ux?>1$oBLR z<=KYbp;$3GXDg34BjNiDpn&%&KYTpR2Hr7UbB)tOSYay za*E}qf}*Z@{c7=C31AIWLPJOMX6)We%lvDydzbhx z9bsavEw47|))(Ba;C`;UO+s?J<8CXt+TFO<&z)e}b*AaFtYRpl=-f5?XUWOddsu>y zA2uk(r%WB(c?ax#P`tvfOa|2dLDGIb+;#P`P8!Aw`1<&|gc%An*4S>kwsSJA+(l~Yq8&< ztm6EqPm{Pe4ZhxuzXpBUH(y-2i903>nH_)YCL~+?E5?sSNdKS{CvO^KZ(wG}Dbp~4K9 zSvJ^kVjZhsI2+}DFxv$n?)Zd+5NNT{KG+|b@JOBqUqO*o6;;);#IhfRFk1v7#rn(> zwUp_1=kj(6Z90Mar4C~N*KSDy7!X;0lDs$-ekLv@#isj=Tr0r2>j9(~#Vm9G1^l|( zr*l;u9QXkiDRcMUz3DhbQh6BJujpo0Wo2e2xgKE0eoM|^zdJX2W30@5Ne8Qj4b1z% zUH$<{s$r_1BUk!tkl?+`N_ z1%h<+z|s~(BZmL_IzW*PVAL;euT-Ss@xSVMz;puC?T*ZM%wWofeR$4|MCY)aiFd{k z=V=jBz$$Kr$w-k|bL2}~P7V%y0Lei8sI{hH?LwEv9b=K zO!2LmB8f)#xv#`9TE~XGHLRwF(bCT~V6$Rf&j5)axMVY!&lDrOXZ46y2p3DY8Kv5U z4~ZGL2z5DZh8|;f^9>Ys`rOu#)n`ZFEQ@pg;-u&%OzbDYEL3RlTn|(1`JP!U^fTtpwV`)a zuB5}6^s&{g)AC`YOoA}dD_Jd)EiaNKNNt(zOqZKLxJm+cj|?bb4rOOHQX&tepPe0$ zq#nNWf2e=}RB|;6tgU{p%H=ZHO=HqYb<;6Yg-)2pmjr7~>NqUwo2EjF_`Wadq_D$3 z1D6jRUa-l!6x~nog_G4zfR3So|L*J@#!sKtY6q)X!^?6xmZj2da!aRi_H5$z46+;`$b25k@q*O%EH4(bEl{3#u4*(V> zdRTz@hR5#i?slH3_Tli{hPNG7z9HpfysK|okHBEF#j}u%WSHWQ@kObm0UU1s!TySP zi5;_#zXRRm-pIsJ=%A5LSMcrd%?%mfRk)g6Er}WCNa%ahQsn%j5oH6As5c6$VXlL@ zrSN-^)Uf@QRjPYZ6{%pji*)M4T6&%?SNs!?YzY3!ns>|B*d*}?P~Yk!5fJ%mazHiV z^s=5z3lZs>>7mSzQO&QcL@YHwmyCZl0?7O37O({*(9$z=_jfL(u~FAe#KQj?)neu* zV!8WsUhZcD7LCN*SaLhMqzgh&D?mg|auwHb_ObdMlpd28TVq)q=X^kjJ07isT-hfq3jm^y$&WMc=k=K{P-#96|vqZ)6klbg_PG z)2n!jG?pWlp@$(BKb9!A%QTG1GXIqopuBOgW-qSaD#uVmXxHW+8wMNnXLN|nvYxaD zzw|m^!hHg_V@D|+qzjOSwtkH`>h zI|(wRrEC63+X{yRUI86JY?y26t|XiP#M+QV?>V*ScDXozOgfai{+?9uM3e2%ot{** zl(n0~^OSvR85i93tk8V(s&88t0R~lkYZxBA*Cyry&~=u~$g@FP!%_yE>&|xh8;laVjOALK?R_ugX%!Dg8hsV>&CFdMzQoh%;++KOEw0_fc#)G6M(g&)|=USzx@*s2c72*T>FzHt)@ zI;EQQD!Y*G<>e^qWDL;%k(sYo1Wbr^I0sm}bNpfQ1u3?78BP7kB-HPF>2zRty;=~*!)U9kg&R{xSs4l7f-KhY zjPGuJ$T^Bl-~`1e(R5VqsY<>EEm-F%+QDzPuM$dam5w~{0q z7_(ideqFvy=xkZw2n9jeRp9bY@u=YO9`M0+PaWfPowpNb&(4F z270^X0xGkbYeCEptEgNjH&uY*h?dehVxbA204u6n^5^KAi(+&_K3TsVpprdjBY36g zMX28Ze!fY7E!9N_4A#enNrIE2-2<=RKZB-etb?ElIkn@@C~vDPn{f z-cT(6@MYGzY@sf@arg5epXvEsv>9W2$Wa!xdgUisx9&2@KPYab4{#3%0+h6%_jclg-X z5R&}cQLBi#?eB*sDq8>%PRc(a!ab?fczVIcG=QXwnx1tT>mk2jtH!tsk5?n+eBlP8 zAVa2;7~(%-CHw>pPKY)^IXPEtM$5Ys^tN?^eV3<>3rFa3ioODgG&wBEA2>kcPi@L= zw4PPKU2FT$#~<)IE&^G!YF1>5#JEopAGUylAB!4RFNL8LWJl#`rk+*(R?^mm(|C?~ zYUe#RJ`Z92YN-D}riKR-P54Q_YfW8hzy+mFhhl)jzmRG#Sn@jIbHq>c2Q`yJ0~5u^ zPK6umxp|#}M)O5VKidk=k~Fe5na3-=fW}D7c!+=VsK}#HPOZQYVjI#7m(}4u!maai zJ|7T6kPZ%6t$PBsvvJu_helC_iGaAQZJzUW%PJ566a zqub{Sh7w35_ulosBWfh+sCYCRrEQvUIm0YEsQ!#;S7X*|jn^&KZ=>!Y8o20IH&Hu2 zo>OP7>8f{>2)GNs6Uqt}Zj@XK6K){f(lgmkRm`#f0e33K1UF0xwfV|_DpDuvab~6D zh|P@}FyzuU^xsXXzK1bS5k&&DSt=D&*cE zWB)e8dWDenZ!u63`p3o#>iH{#lz>A*G{W-oh{+1}dYk|h*PpZX9zwRmcLVm%)D{Q! z-OI93{RFxtS&C+tA}hk!<_e%nAZ07U!AuWk#AW%S@j0y;Q7^pb%{<`U0si9zYOo zEA<(3u>N466nU?Q<`Zs#&+&ruvWRLqSD-(rD2wEpfk31D&cRQmAdmEbc>Cd zlQVB&>Hvsyrdr>FD0PF=5m#~}HT{7#<5}YG4{KOP>(kA@^oXoyIBCh|Ov8 za_~U3MqyAhUw=h#ADT&%+=F9_<{tBFoz`b-RC!rpOH88^&z=$X7*#kjYr&>Xf4`WF zB#YLYP)KE$cJ(Iwv>Bkh%^iC1jb8zsVE2Id(%%zrh3H@X8L4#R=1smR!63#Hg0mX0 zfnFb5fR9f7iQC9v!yAZT9*9lk)QbWSTEP@Dg4li&dqy#_k*5?Gve;`d@#-zLIiDh7 zR8x^zGjY$=&V*Od(K_UJ5mw)k4pnJT-z$xKg^nbR*P+_SCYJc2%J;neqR6tbP6}|g z&D|HjrZH=k4berm)ABS;>!*0CgGpz*1=Horr$OZy^%h}azqbXiA!#s5Y%g9?eVMpA z+%n{|wu{Kq1O9dV|NF<7P$8~lf{k8y>rYQ8O>gc0X4+rCm*qtK5Ra6GbaegOOU}sh zApAn=Fn_EFSetmXf;I&it`NK!D%|R!>igb^ZqiAW@KEP zO~4~CIEA2z^$MIjN6gTH$_h}rw-C+)5OadO@a*#*{j;5(_vb$p>3F0fjN<4gApjFK z5U$@KJUs8vL<&ci9FP4E#JXb>K&i?vG323XEK(yS-C!?eoF}$6;JIE?)+`nK_uNny z`wwOogzu?4bm}WjEsrt44>$p>VFC1Cq z^+0+BE1!_|pFaSDBIILjFppKjgftvYyS|~|HYSrSgj64&EdxIZ)JUA>8e&NHjVMJu zZ8}Z-F9@2+BLzSHklc)eDVa9nA(5d;m>-U)+F6~+SE2ag^~M#Cn{yo`{0dl;vW7&? z9IwasZ)ZCt3Qf;sPid0p;|mVV>bHs;F;BUI|4N^Yii;?MoA~hc{ur`Ie9Oi|Sa2S8 z{LcBl4CFer!#Ne|M=aVx_y}r@^tWX%@^pPz48fD@@aJ;=&a0(H?hE-(bYOBKH6JeG z=MF^DyDEQsI+2)fbNPwDUa$!Nn?=21Xk-IFQh`4Dy$v^jO3H!9eM{H`bVliB+>-&c zc6CT4Mt^;Gv8S0IziCoVz5vsN=&p_Zu?X^Zq55uy4d3>V>h)A5Qu)MmUC9z*$=e0` z_4ROiiMBnEN`$iKm1D10Z-wE9+3SMns`bM{awsBdWjEhaCOi<9_Vp(?7ZbSQCB>d2 zY;qD;tDNHplyjX|LtE!W0a6e`lp|y;x9R|JF=cC6xj|UW-O?~q1^2@&5SRduw0wN`hL$1Sj)yHO2pGUk$6b`D{*JeCcU0Zwa(jdZN> zE2pa-qq_IML9Nm_alBKeBw+G!J08#x7V?kQ`#`7XnGc;0UbYWI?7W^JKqQ!@7ES1S z>r@2N<<}x&p}2GOp)q&`z27XVN=@pP5y?OjAFlPDL7jEj!Sae&K0!bYBF@+v%I{zk z0uz(OAr`~a5I&0c!=?l}J0monwBDx--`b@43!p_}GX9}tMRx{Ywb_=91%U11Tb9T{ zO+tpYEom_^$!NEjQW0>PaG3DkMPoc@C5st&OrPqq_)&Tlw0)sL)QxXHMGy|O2Oc7c zLI~SCa1%KL7G>sx^&55Iv}@4&ipYM&_bRL-`PWgf; zqNDm3l767v3x2i$asQU$6OtwB>8YZMFho-N%!wN0e)$whPwV|o&j2KR1vP{qg!0XA z<|CW$e`|?c7_X^13j1OrJOuz8m!4qwXE@s9-8p(<|MJz1-PA;?}7 zR*v3$-?&a?xCMS;j%o#g=qN-(#@cfKw#wh=hI#!jDJdEOjka~ntVw}CkPTEX;0g?` zbRapsu|HZZZh*}%i9pcrI{AEqzXQPX4mrq0c}|g1_uJc7A%tec zh>k_^Ooai`(Ku+D-DDKZ4r+27bpS3G*6!PSl4(ck5BBy$IUNm?A!L%1)n0fV8`$Bx zGBUnzKV)nQ|8~t($7fg)V`QeMrC*2XuhC&St(GrCX8w2N9(jng}4y-XtC# z9+uz-dtX#hF7<=yEOCGhKy?fPB!4Zo)t~<-kcu*1zTC|JpxS1L7mS&x^4{{^LrETP z$nqh8>?H|_F~G8W6%e(XtE(X5O~J&>JoMKL4<;Y{$06P269nofu$u_A^&N1aH{%ab zvoV~$d!T>W1c87lY$Nx@v^Qr6al661f{<2_kn>HePg#)SQR8|AK&hjD1S`ixViAWF>mlO2#hza-}=*-%yy?_3whv4uO$OTTTXQdy8L3>c~}{)YQ|asO|+GMj(*^FQwov`f!p`>g-&(Eom5 zD%I1iXW`-^nef_rekJ2oe7vnnF4c$d_56@~_IJva|K;>6mxQVTzRnBs6!eFDpw5Na2nV{mUBr`ay$W> z@86&Na@m#J9gXn9C| z{rYu9`wJJO=~w++-KYtN?Wtj*7Z?pW{U40se>al;oKAlKx6Y;OD#m!mIARvj`;#i1 zSIaUc-1PQ(gud!GqE6c&@Sk<}FK^wly7#~F`{Ytf|ASrr_b#TAuR#uW9%d-)Wd5xq z|NB8s{eNp+lV+(Wsg{RQ7PGwmp=X=^54!p9b2P8KVIP%uxbL*?Ld;W* z_y8O$-~L|(Tc@#%PHg;K?Vusn=~+7a@5KD?&+eRMtO`z7luwGz%&Vq)_nD-;fRQUG zZNyinnEA0kt@oaD4axff_svgAQV9p`;h&|jUw$wQuaX>o22J-iPwKiXg5RCISkb#J zXZWP8$`EM*hS{i4 z^&idOu55nai0KU_(8ZBn*}%ei5|JhPlGdKFlR@T#Ky{<__L1~mPZLbv#-o!@PWY|_ zisMzjm?Uw}A!?CVvd0<5t-YS#b#8E=hMGXVLndjSpK!IW$%Y~JIi?*diQNCy#T`26 z_muy3-Tr6K9GcNpDt|DaA&n)ax&WOLmXXtBVVgJxEL1O--IBP^@E+OvOOrWYAw01! zE#!4hRCM-vjx$qsiIH0Ja4vttqC_>W{#OX$eoSZ&w2C0PuG%k4>#8@ zwEM~ifx6$2CFz&z$=lNn4S70L)u$9(#~2jcl&@c)@@+Oa_#ZClaIZ}DKw$)}$6z?F zNug;`_E!z9XmAGgCaV6h_RxLS?VNee#&oWfS9HbKP!VhFJ^SY*47uAT$D#~A!)u>9 zha7YkRT@}SE|!+Rj9k3&Nl=^DRj08+@w(*MDL+~ttt|YfsydU>K8D!5LjDCVd=cfV z3eIE4*tq5sAsSAfE?)X||3~K|1_^>=x#oskorDQ;6^rnuwm30T88>IU*OsOErX?;r z=l{nFV@d}#qUrwkSvZnEf<6%3In8SSZl99)o~5seBZqrr4mz|}kGIE}?j~cE;=YI^ zyLBz0&*LQL9sz2qlk1v~7V&q%wQKeL=gIHRz2mk%T|H7(pZLvu{ z(*Xv#4<&H|*W7MV>5ePaX5~+&k+H7s9%i!kykyoS)cR z@Hk}3CRv#F4hQFO9BcY{y%=r2f10{%bf*oa|CHp=&CD;KOZ-^be!uL}nOo8W>2}4| zXDv$e?f8i){`Uh;QR16x#Th(o<(Fer!C*>0SuZ;P-d*H)%G`?_OCVE83=>PzP>+78&Gx6wr5MK)|wtr~+SK>^5*#19X8{E8p-r zKgyaJ3hUVmD3804{gF^OW_u&l)HMdM{?Q=gm_JURd31W#_ zxcg(Nj{s`nX0WXw*8Vm|Fak&^&%H0?NScg*yU5%GsQ1Z1Z(Rp7dD?ltZ7%&Kl7M4b zhmXZ9+LKNvMbyM+}$`$S^7;>cryLhjx zmA?KcG7e}~UWHagZrp27LgyJ&O(SON&0_uY?=F@zK-toK=Ru7>q?)CJ4@F&9f&fL0 zbLrPL7av~p(z@i}UF`#HPg_ZXYRU2s&|N{M0DJ;?YZIW4=;h$n1Yw7Z#Nr#*4#qy- z1HKcmT=+*o(@1j)`$_Vu2;3`s0OXqwZ$-YB8V#dh*CIEN0*?+Ea5HV&Zd@MP1k=d3 z>(MQ?(+BR#ULfI=A2=n&0A=pQ+W<>OH+Lg#={6W!1Fjz;06361oex@GA1TY%eWaT> zJ0_CYQM%=Q=p}a0(a9E07s!6;yOS%FP)I4Y!*1d^eQ=8j#Kp_EjQ+Wkap)A({W#cO zw)p81ygkpNm2X5mp(2!&l+mS`dgH`MC~pPzshnChk?_8gzs{2AoqY1Hm4=w{GtKxJ zrMh$VWSdn7a^&TD7fy^T6q*zBD4W$Z(h%b2-rJ-4f*);s`=koqOy~WO{^v7oMQ&TA ziqEHya631q8k?P-3Q45yw1R=+Q44MK@4YF2c$>iit8BCs4$bDRYr-%f<|#)Qh{Mk^ z7|+ARhSRfvG76E5gAcBL(?|c2o9O_aO@oc3&?9@h;bMG$|9*yfLE^0^`>=Nc;R;cM zYeJB&5tC%Z+_F~-VkaDPdW(5V)+pdk`KVdA1SP=7g+^IaxC9^tx7e`;yI1)EQ69O* zqe)l9oM=v`8*x`b8g!?yfYnse>Ipp-uB~^8B;_Qc3-1iVF8k3D8gbXv&q@M;Dt!b> zjYFT>w}A&APp=YU0T1jX@Mi>j*i(29s4AWl3XFoO@0{q=VN>7&VXpAj))4~4Der|W zJ9S8$JPB1&BSe{SaHZ*bdeanVn`+#|xEcSKaPyP(`RzLEIk13fLZ}e*{Tko4)ngWx z&D_dLt`eFbBHSW?RssrPH2^}=AYiUuk+=_nu`Q_!<3$i~R zx}Wz@cKAgd6k1%Bh(0~hI(sWtuw8$*W4nHPi|@<3kARX!0`I$n!;g`W`yJhmGfCY<0s*j|6VF`fF!&Xu|R#y@?CV&VhXs4f+J0nsBrxJ zEtr8*GAK|#*#=!aTe}!u)dl@WkyPr_KPyP|Yhm#Y&~~c8%Keg{0!D5$CtnmD8viqyC}t}qv4z387pGqJj9nSQ z{Y`vDyyMNl37Gx7#?GH zOg({i%L7dBMyGB^Cr04*EG=iX#+LAT&uggZnTAKPS{o|85+S>k8yuS@2Fa`D{zQ`J=NnbE#8?+iL>Wp|Kmp$72Huv)5R4u6I*{BO(-UuD2~RwN zs(Ko%OPXM4#WSdP-`0xlPkI0YZ0^k*AwALr_{hd1C)tu4{FsR03*Y&=FC>JG+|`@F zb)wTYaEZzquV)iQ%)PdXu>HwcOj!M*w8|4R;$3NwmYK%TlY`H+Y=?faPndT+o+ZoD zbey~|$uuu(iF+ECg#H=gk$s*k;UxXZ0My=)*`vD0SUlNUuhNQsFHX%(_X%i4rB9#E z&UdddMC_x@eQ=eiZW%}WE(`B}dl|apza@D8Ytz#g?rYRVlY%fi0QZv4HW&wa*cyQG zq%hb%D{nlhKWUh_!slMEL`9gk@xV;7%cA6hZBt${69y= z{+yb)Eiy}vRMna!Q}b89^BGViqNjbvVGpF6D*Y&#y&5<`lwHrA+OU5UHE18JBOlP5 z2EHwG?uAyUU4KTV1W!8xzsurzghRs8q-&?M$v1!AA1=_29F&1jNjw}i{bT-f!2iQOyxC(&(5 zNPet*^l=jFBwtQ-UEoL#XB~^0smXh0FU8gYE-+zi}*Rc4eav5Yg@Wt;}}#= zBcYWY@FV(fo&#+X>;ovz+$6)XJ^V-O z6og?LCISp?eEM};4Q6*ptKNrq2!4;_rmBkw>-k2p8{BRzvQ-NQF^hfE1ypty_u%9T z!AX6>D;whPQFwl*Y`(Kps-KnRAhgi8ug96A#{v7GGMk~V=Lv9Wg-OC;gs%O;}Kr+8#~5k)crsvJ|l{ z<%Key51}ZSasWg*u-zgipU1M%)@{ch6rdR&XO?V8{OCu`P0_EQYy8%e`S6zGNc?iM z2=S=G+JkQ%&e($D3CN5lU7?e*gk{3ZbDr)JJT^_8q*5=dcgouo#q%H^5~%0?@M&9g zdEV_59C)nX-?w!V=i@i8#dWGk?l+zC+xx&{_9=RM>%eb)%5%S_zB8OG`J0<~tv0)3 zO%$xjF<6sn|8q?i9$zaxErYjT(_*wsG;M&j{6O=qeh|T$)`>6)4itDzTz5b+GIdwNo$6KT3Q%rNr_y->Q@j8B{!9MFH z8eE$t)ljWJDY?1f06_0Vuu*!;3gDaPTn=IYi)Da&*w-sY@B3Bc>i7Q28GD< z%#8A5Uq)Qhm+U#O3NIH_T#|G)7k*q%EGftJAjp>_P#2w?iXwy^-Tb(ZhKi8&GeUor z!RvO&C2XVyi;38fEGN1?J!LmVcvy?`htV~5aDO;oOgM9G zrSD#%H}VjhF__Sermd!^t>jd&DOXmDnRcW|C`MCCeuBRG-&$4Dsr#)|fBP*;3{Vec1BADYJFR z=w*wsXcVBwqCEkR$Nu-oV@O@RtngCp;lwU^?8|l*P*(ojOWJhpaLm*wvp*#@oV^%l zm~Ta%yq(D==c*A{9Dt{8QGtw z80N|~azV9%i^GNZZ@yTgTh)XW=K~W`YP&OQyx+0vF9_cEl=1VN(iFMgmn_$3A#QLP z70zm(Y!dTAM9P`&r!~imE~o2QTntOWZ`gJg4k^%t1W_xSy$6M1q2mggI9K|Y*|c&j zm$kR4(5Npc-F+Wi7hDT)&)V5<} z@+^~l^E#&F-F(amwPF-V3$T!1rxTbd!?&cW9eu5_;HmnFYbHc!dQ}}+A(ckny%WA{ zWpdd|XO(3A_N<6MYhC(G=+bm>Cel}a`1RdP1I@9!;CHtMT{SlVB;PBY!%$9Tdp4;T z-TfTR{1p9MmhRxQVxQ!aOQ8%2CLsBmUk4Ui{C;nyTJIf13L+qCq3O?b7RQfYKRkf` zYk>%jUAjS)YaR?EC{=kIxn%o1uvAy}*E1S{F9FNyebLa-=>)%1Cim;6m=T1CtBM}3~%fC zp!son($wL0im|3v%a>g~RRcQF;&Y>`Yqk*X|T zKhYiLx5P#zkpaW^q1hw`uH^ z$;B+8&9McOoQdVEekls!=s2%>(D*3YX0u77P;3iHU9G_Inc{>grUX??v{qkQ0Dj4} z3Jc5jHAXSVgI^#}>GPaGQC0FqT=cRj+AZ>Kct$`9Ex)B|lWT}`)uba!+0e)ZdOED73juDjYyOdso%em&Z0bu3eEyk^T`+$gz<(VTIW{IG>H zg6=b)Pn=A*e=Z|bX**UtguZfTkj~)By0-;YFpkZSSrX*6PoU0P2Aw66Kq6&q1~Jr* z$AjtckNa{9aoUL|`IYa&=G>oZX{*f_WZeD*^kuH{H!d%R%pX8;Ab9vvbb|r|vk;&8 zOOX?+ZbvQkW#3^&5miOK{2i!zD}E6#$bl+?N&jc$Q=IY^Abf+}Tc!uL9}T>gy%|XXSXpsKeYB&%!Fg z$`apD`>Q!j`?##`-2^w`6H!bL^x>?&lzZ}0%e4D+yX}`(;l;{8^6qU-Y^uCY8Mx&+S_n??GoVFz1=L6#fqA2aYmS7%t zHU*Q70m0MH;!t5;>-uV@w@$fzl5${aieU!oRx-B^bgwQ3Wb_6^?r`umYnUkdduwJX ztal@q;l$nRg^neoVGYcq>G6q4LMyC`D2nl^(skAarKr)7F_(MFZstcX)-Ze_6_<#| z*HDz>6Ks7egP4eR;%w8I9LILoGM9N}thfgMo0TD_DP1!L*v(s2zL#CEK>bN|uF<#< zW@A4UwSBZVb(&xx(==4HTVN%`ZmL(r2Q($a#R0Fs+N&3`x_<|7b=Vm^{0vI0`fT(x z>(k|e&kE+cWIw#lw6u~S>SATj<$#jf^A5I%m9-tK>^rt9zvxeVvAZ<;$Wh&VhldoN zSLCu7T@5~ZJ3^!YG~LX$OJ1UhTsS8K2$x4gEAg8l2((gC&UWYR>#zjrZp(%;dy11U zU<|5CmDwYByni~!hAFV+qj8eRD>W8wP~@4S4fnw@_^S--luW0FtqP`717^bmVB3RA zwizz|oG!a~*dy5_${@2VZMz^VJ^UcPF&QP*%|_-8L`kjH%lhVF83CeONj(06FNkb^ zXCXWG^%g^Cqn4^_cG;IpDcM%~?XFCfR$=Ek?kDGU_6Zn-zWh>=EWhhGdy~R7{x;Vd zjn>$=QoHfB(g!204g4czc4Y@uY=gpZ;T2YDTy5-2!Uvn9M$5W7I>$>x#KRx|S>W+N z<{0KySjs_XntXajEm#X857O+CAhvMm8Hj!Zm)=)ekxX%-1x7HaVmVVQ2pJSw^e!aB zL$5YRr?3H#WrZr2(d7@KM-+!QUW?YlR$nQ$r3a&2mv;)u7_tN)q+w?vN8}$iKF)#5HuE!pR&31rmB56&So~_b6Vv=uTLTB3pC#{|IOUvRKC?1K{R+CN2 zHFep({w!fY@duCB9&X}i?2-|!lHaF(SQGYkN7^<+@JI2HXrTOnb}E~Il`N4brE*cT zhV1kGjRxu+(N~&0 zwRnRl?Xm|GQ>AtzQ*3nlgC!4&!mUR#quJH&ws!0v!0oFVDcs`8HcaX4#|fz@I{3A` zL|3uf(K)>H-7878%60XpWy3FD{E894ZC4)@8{I%oNZpUW?(}M(lRi$nZAtVtImzxd zmyun31*Xj&)>{VJ7B8cDJGwzQTRXqh3bph~%LRpY>-r9Nj&B#BjtUsise9QKu(&bi zH~KI7MOL#?4C3pSY<__!{Z-49+qN7EPhKN|ojV!)%>_q8+0+r!U9GH(q)8)(pkNyo zWam%WwvOdjsSi6oI=Dk?>7H{@K4h@u-DEe2tU1mkcRevo@je78_2@|wapUm&cyuMw z6^^r`_cP+n^S67aBt3U*XeAX_-wh}fd2E>zP+v)XXCLH?7p&;Yz&vi->qrda=yw4- z)S+F7UL5UScIM3Da2_S+R&ALF>;0mc+3dcBt=zf$3<7P>eXr2`yk3u2vj#jsH)uT- zCbg2=2f0>L#HqH()TgK2d^5a*@qP9>ci_G^68c!}lU7xA?)T^DYDL``NjlI==pe!2 zfy~})`AeqvrS1ZmD0c1AWx0XJ9b!0Fi%E`u_HC!T+^6jjo_SV0o3W&a_RKaYCVKch z51)u4tt-$l#pz}e2ZuuQ_tclSi&P^^Ri3=)671XTFU?$fX_(<;*s58zSmG0lFjOH! z?Hno%FeDhpWw@Y0rMRr>neA-^H2Dz)x^qN}{lL1NL|3tWVg*^Q>d3pv1Q|8G54Doy z=a;~STS8l!o9G~IS+|wUfJ^j&OV2@CK;+k=UR=>P6Cjh_1?OR%$JbZ0oo+RcYv&?K zPN{UhMjr#Zxm$3-W0>WrR?qgGEtQySPDwbnAWZ{X_pXxddp5}|Y}LKeo31s9ScMCs zq#yWjAkla2u~6yWCGr>wqZ=`(m+`Dv{gva`s&&=HGb(qTQ*xe3dp`4sLo+439*2Ok zV5HwxOsaKFqpL4dzlnmgypiL`CG<{hR?tZ4z4!h$qouia2Rj$@OrFcEeH;F`vSqz7 zf)Q7i`gw)h@mouL;=bEp{&2*>BEjWus##dz*Id8LXC0jcjO_*0w5Pero+NFp|IS-GA}{LtkaXsQSL(`r$H zOl;$}TH6UA`|A{4>z(=qCfrpZlQ%krn!b+^!r#3`ZSVXaKdkv2^_<6}lh8WtQkiNULt- zQHgcV%n_)u@0_^VCo1DFCB=sn+huGytb?Q<=#uN?fS@^w%%~UHEPcR)LP>zWgmNWl z4)&)DGFv$!2C2ZYC=5utU#D#EtPJ(YTo6qpSJZg79BmecpHLW@$Bs5uqo9`hrp`2+ z&YoZW)d1ky`nX>meU{|U)XL{#tn+S^F`&*YR`&?rr$yr{FuqYN9tZY$1#qT@zsp?d zN?YdFn~jGi88pg{mqNAuDN1teKhvw=Xu&kk1j4|9uhu`rjXYQlQH!G>Tv02N5S!?H zY)Sr!;_njKqIm7?-z{z(i0L4=kJaLG&3^4Ouk=Q0c-7M$UFOlMrF}PlEW6%rBrjv# z9>?v2`BEIw+7Z{W%fwOf$JIeY~g{i+#IW5=KRT_GYHf&LU0{LIJdGg(X+MH?p zazM2nfe1wWQG%Y7=!>c?*Xcf?0`wg|QcwqElYdaVAT9ej^P-=Ra^B-M$f|Ok^ynH< zN|}CWv@|~#4O&Pw4L{xuwV!DfoB)|eU=^o9rOxI2eCJ3T9NNv1oPw|pTePLB>NTuW zIE)z0EK|BIy$q5TOEJyova3lEPak~{U8FcvQ>c+Nv&OrKUTOJ^5`57nsZ>3(khNvl ztR25BYJNP({_Guw1tt#oR)O|)3>gE{N{BAC(H%&=K_)hiNk8jTO|hZc!j&>(kgdgG z{Q!r&IDFVmf~(q8$-GqHl3qqYl6sJI{VChP*SXd*X!nQPOZSW_nTJ0_vUa^Y>Z-`x zUKk88dH^N)7{^8gQfuZn$cbLp3maBzE(UU39&}-L&ezt8&NqYyTGCnVLZ}$=c&Eg9 z{7Y>3dVB^lH^f%Ml7goDLncx2FTL6ZQ6JjkBbZYbyQ;G1vxgEKW|8m#QRSekzu~|V zkgBtm^%yxy41^!8W*;5yr*1FrvA%N#pZ&pQQ}h$$Sjn@Fm$kZ7!?om&A(9)(=hgtl z$QX2h{-*5S4A6?O3IiX#&5}BvCsV*k9ob7A-?JFOS zo1{UaOH`{!@rvQv)0ABYf;V=<`yM`_?E0BO)~<)+Yp))D9F>$u4R2laitBKKC2?Bq ze=G@?4$M-ZXd;E4Y6*$U#go^3NcHMj)X{V&JWf;Cz`}{p_^yl-tE$w_6cMzk^2_>& zc=x!tb}U_aM1KqSpprLj*Kwq5-JV8^O-dCf){FZq1w84*g+Bh?7HB=&>h;m$4Itlv zhv(x{t?hdm>)O?i#cJ>4yE(L;ysCd`pILwAFK&^VvLpZ^3m51Og`=;E0&Cw-;m=b~ zT;RpALe0&P|I3C&!Hvch4t;;+M2M?->S;=Xztc7N@ovj<*0JyG*n-GY3j!fI&{&5Y zZWJooF%ay}A)|(~4K+%v&~Bi6E62uv?GLB*?;oD;9thgs&zAf5+ym$rBN2WYDU5d^ z7Q;^OuRu|-l_Tvj2yvY5lz*OjVx}s>DW&DK-2Z~`-s?F!olaUi|lU(33wo@xQOnocvRp{QLR>?*Cuk06s16-5~4U%t-5r4>bpX zKKfN$Glh9l3~tDttmuUV)7Y-RM4kN?8rL7%yT5CVYyB7hT$&bA zpkX_KC63$WM6V)GFuflG?Ypl+!~oK025Gd4>v=J^wH1T`PWP)4ysR4xKw-0%_Quc= z(nSJ8(nR1u*BMSE$(|N^FkWrU>G%b7{rZUOPx|7aIe?;TP%^toco0)FiRi~D^rEF6eV`S0FIfJX78)z&X1Aee zgK6Qh8x{6Edck~iMIWokw_^a5B$Y7jrEU!B1iLSOwl3i*>h~Ue%#W(52458frPY=Ep#j`f127xXPm=N12MWJImUV^ zXTP}SePbTh_HXIM1%469+{H3A`#E@1ZovGnJ^)UJK8&9*h+;#JLW29igodsbW)ZZr zUD@Bw1k%N@(eDdcU~$DABybhrb%W#f`?h=X;;;bbB2ot5GQQnE1dYovMCa9aF|TGv zuMCVhaKbN?X_T7-DSItafxZyMhpK1QOxcq^4#DCGU%^c) z5KhE$oA@F;tx=diM$y(z>+|c#E!!FNZgGF$I`!w>pD^uF6+tm((zF9d zaS6av2ZBCeAW7Eqxn?Xe&?sSGenS2E3-gwEMKD>i5)>BQn11Jlu6k@U&?>lZJR4i| zz;rqHp|x?GeqSKCqm0SFKf5-n`Bt9d-tp0{5kdQ?V=G6VUWrwG%C&K1E>Jo;4?LC9 zs^FGqCI2~EP5*A66VrkqKFi;>Wc;)__PdoNBYYL4C3p>E88mtB+xB}Sd>Fom> zQqcL$mVo9?P3_(^fg6mDsYI|kYk0G>kf70*!BIV_yO`B;h+uc~kn0LXSBcdegZ-0Y znFNPku{}ciVI#j&YJ;CZ%}`rA{6WNNPKWt5vKQb=j?B5iUcwYV?Qykygb)xqt-q!Q zBVgz#pi?v@wij1Z`h{Rj+?`ISq~l6CfM(~409f;P<(4&Ar5lI$c8JI5>1uP{9laH_ zjZuEXWi&1TGJ*8>N_&MN^3p^^clvJlW6e18RJUaZ#fqQ5S>z z3ANDRXqr>l0@BP-!JXr{0gvP@`T6G^iO-mv@;=Q#u3KmvVi(gvxY`VK!EZl6R`9)RWNpBQrY3lM?Ez!=O@7RMWa%==i6kyr`7PdW>8@a+PRU{(9; zj3r>)Y%*nta6{b&<7j@84F(kcPrpRy|gn237!2vkOrnoH=0 z6OnmcUJ}TR?KMb}oWVn)*!FW~WdNH6D-%LK(50YaU{k$w<<|E{zjhz>XVKe=3{(0) zDgFR1KYbj!MNGLhvSsryH@MXHHow=aNMO^I8~N;iOtyj1X0UXYQB4b(n*BzBpfu;0LKEXp?Op)-+z>(ITm=R)I(V)HHu3trtG zPRRVr{j)dNI{R{xZK+|Lru9HxJaVQQJg~ZhQ<^*krzS@Z^too6 z#(e%OcI^%cAYIKWC;HSU|! z{gHW(l~b?1n4m<zmwc-MEks#QU4r~oIs2b~GQ@4{@kUvN?M zES*vNmJpgG_$~mTlv?asInm1ym27Ln)`3lDF>(=@r3G93`p9j1XRB^6N1^LXw;+yD zO9?PIO>?{mLPWxZ3Nbn{H0eV)ps$`O0<;bQ6dm@b$yQvOUtKcIu0B-BR!#C%wz$rg zuk8P*R}wX|=!JTe-{nXy{MF~6;0{y8k=O4-@&XrIFK|XBV~`0|wz!T$M@2V(+(S~f zi1@==`J1m;O&&!)BInc}Tm|*7tHx{?sQ2%r;o&Ir-wK+`P7&h2m2}%!Vtu{#mn#g< ze2>moYpxK@+4e%G2+zLK$WdwluIx9^)-Q*NF0pE-DMcCMSFufa0pPe32Zix4H)}#d z{$pR!w*slb7<4Az;DZdd49jCckmf?jue?z0l9g>_y_G_2nR}2PYgd${4VmrTR`rb9 z9c_e>zuenzM)Ola`t)jSsq}#F&W(etsZjEkE>~-*-t@DT;gwlVMo&mea`Kyt=$o#OYgJ=Nc^v9?#rTu4;{h0&DNF|IKg)UFs0`K8s*9M zV=@KY$PQwiV6LDTNV;uAbqhPNHk*~6pjO{^G^?U?t6@lV|L^*cR@nX>&Zl0=W2*bK z0!>IYn#}wR_o&} zW+Y%F6mMa;P@1Xt8NhNoMp~l!)YXB_To}2D6GN`5h9-}%g5&3QSzKE}w(gDjq+#?F z4~{jPcAiaC9Dm!L9Ajl+oSWpXNGv~+x?v(va}GL^o~4NoA9Wu$e+PK4vTXceE2iF3 zjDJ+$QjFyidLTW3$`k76F$mEs0uLt$kOhMP;IiMZY_Oml_jhHugc3=vX?6V)^IyLO z*+<0Kp=#aFaW=!BhZ*Z~<+Fzzk?W^^=qCKlscxyAF2M{u4b<0kK}g939h~S&HA_ZA znw`(uX{yGIdfTMvmK&uZA*DC!{WnwtGl<=Zl9I1A!sN|GD0||9meuFoVg+q&?);Fx z^CVfZcRw#lFBvVX7FZo1a+X7uZX%&np2C|vM;QDXZGxh^Hk32r7#}o%eo22tK9>#u z56x61>d!D%)S5eLtCxl5=MCFrRn5)>i7~JsI3pe-%%Q?~21k&50lGD@I3h(avB^>K za>FF?**?*1IP3@^Irgb;@J{8#WTouoZ^G?y_2@&1RZD^R+|; zT1ueyuRJAR_8Ks&q|{)J8c3Jn6aCf832IRJ-d&Xcr{47L@pDeg`@c8dpnT$T#cbG} zbv6lYHRv5DTrLHnvkUp$MR>ppXJSOHBC zOz|yCr!X1quj)eRkGiOqQQm*C-l{eG<76fw9(*ruFx8 zUTxkoOgbXU{X))G7XP~``uGRz^-mmcM$gLsNrxi%(WyabY;C$~NF#~YKB_cETV_)D zU+&e-nNmjFm}na=qkBIySTCdk_Myv+N3`4vz9!k_q^qv&0t16i@{PH2$*x~A8$yPa zuxRm#;4dD6bTh|K^3t%7KZ#tQ#94fykI6IBv(5svIINlia?)4*iC&}~xvvy9k3t)b z11}lH6v@VW?=+DEIlGrSWctQzHE^%rCMC@v6WAN2ui&L_yz~`74a#M&yr34nO(VDn zT?bbX+ZGC{+sjiJ0@V9?QukN)8AVCW4DOre*zPZlB232?&U(@ugb4^i;Xqn+wK%t+7;J|+=CwFj&@R_PBYIXs8^g| zMs9+#k#o1aT42v-;?IURvm~|oWEpvJ6pOhkuLJd7!`cXq|Ah_pIWwjkPWEN3JpKDy zleOa9X)0LFV zd$XPf-KIuCNkkxE_KDE?B+=$JXsj#2WAaqzVK`=B?{O%bDN^hT#_K}HGDEaURyQD@|W+k-bA-7Ia{OK89eWs{Im zrl&e@#re8oOw&x>{TSZ>*E7B7Z>1x4P7kU#0{~jt`Wo;gsr zV55hA0Ap|O_5cg3B+I@PEKu+EV3Je6NEKRIvJ*K_+^~~m_wEjI^zmwxw#|YXk zA;zdx`IAaoNW}OmbAG9}^VUZqT`(?2dk0^qL(-TJAbRja&mj4bpF$k^xmrdAV%(Vl!4HY3$S<`|t z*6b14#=fdG6!k#F4Tw5V7|_$wv#*-gl0TomT>ZC8lj8njm^j<xRDU0Ns^{!0Zg|&_vaFX z{<#FsNsNX#o%eiX4ix@qNEe`@TVADq=RNr{j0;T!z)nocWy)myZiLw5d;He-*v_7~ zA<9np-dA;}2S(f85&xwXSZpQ(MyS%<-J#g`(*mm1zAndG>FE2}+4mOpxuB*9e6aq(H zT2f-8*oCgmetTk06JqhV*$}M3=rQY?Qoo>K*SO%%ffIaxaTD(m*vj%B?xG>MW(qzE z#f^2!@=XY{7<+KTJg_~+*Pqn?Z7pZn!OxlAT%6HAq&f0MG@b59)Jm5bSS<9+7qj=`EbEV1Mh z8pW$sz0U2UJiYpm0(11oHRw*JAkFAy!%d>zIFa=P|ASfW zxX(0BnQkpertnj4*6EX5$yMD;&LNTal5c4xtL!m;PE)PvO2^dtZvHFlQ3*sFTUPDc z(Nhb928|S%q%PHg32iOA#bbSS$+j`!yqHZm_VfHQn{81Q$F3Bvq*Z$rl!K$;Oy>xp zZifhUknBpRnZk2Yd{7u{CO~2@?wT@jH@=+Rd=$6u!eeSSAQa9Vyn+b};JtInGG%_; z=uQ3*v@i)-G?eL3>=IEUWBI<_tiiDFYpkAC4U)n5Nr4xwa7-5KLX7_@*0fB_!S`Se z{k)pA=`NX$?^OyvH^sW0y5x^x&Rh5~ndQ~=16nwI*ZrVsfBMqgiscVNrX{W6Vc|F^ za0t(t+~47ixr0A*WSrB_kL52MF=%rZ3U=V=-M;&h3VaS?YCfljF{M60H`Fl;`1#nk ztY4Me6d_3`R&VCm+-eCtjHYQCaW)lcuQp<;&hKa%w=Mn_)gyQ5pq8sjSGK)EdL!{h zfe`7mRYd)>Ob>pqJcUpA@Uv7|lADVQAuEh_w#WToaXdWFJ8sQEuZj zN+zAN(RiAxYIHP8p+@w5oxru{plcjdMc;rD<)HLFHIUsQmcYW%#{pG(6096kOpRd^#V_v{0@ey zk)QZ@4*J~ieW6jL0j*p!snXm8G-o_%Yf@rU2{JO)zR@?)D$RVVrKj?(xFk)Jvfoqy z!!RbEc7#?_FtP{x z|FrxR;MZ#ZhjNwudYaq_p#15GjkfI_pYDrJ$KPK@7jxs!yG{R>SQJ^7PRMNR0q^1v zN~H$^;rk!TV3TiI!2fM3hj)YJk|+k4R3aM2y`iqF&}p>!H?MVKy%%K+7FNO~khOhr z)<|d*Sa#{)-NKu`Lr|B5MFD?1e=kvTziX_y@Y{Rxnnsy#M(v z@IMp9Xn7(lYMpvx|Gc6OyxFr?UT=p2nq;BjIpTp$iprqK>9U2jz+x|`ezp26d8c6>E$m*f)&}`VT7FJj9{r6jO2D{v15$q~|FLJjG zxSM$^|2H>SiV#8%5Kox`g>^@K)Utx3ci$guI2z!w~jh*0=NNVO?N$PByZbXC<6k3V#!uZcA@Mhj}u z0M?a&?RA^u5##(DVPT8i*~VuB&Tq-n;%>KeMj@3RV97MeH7oB1IBsA4Z)!tDqY6dN3{`Q4WaK6twmLk!RBn z{eH7NzxRm$yz~EW`j!cuevDxes9gPjBa)Xs!OQ>?@K5vrE%kkQ@%wi)PXfpfxkWe> zia|ABim-an5Ela!UIcAk5vs5!5m%J?Yv9Qd(0vop;OIM)^oi*+-! ztMXgudm6y0+>JQNp^S3K&ai5!C(Dqc%;!$HVnvQoUJ&5Sxdh~8Fxc@*A0}-1cWgR09BgX*VKcQs5#(B zn1XJ@0jR(1(&=jYJIc;YiwC`BfV+xy4qqOCarVQ-NOy=TSU&TX&6#0*88v1mz`phXA<9w4od^9^5z=!Z__^xYPrNxAvv3x)^%BEgT_YX9 zF~6oA>zoy88&*qf4~X9oK*0Sg21#QvC_<${MEa}DV7LQrS5J1~m$8ap>mD95#qc@q zDEnq5{mR|m8v}>m;Zj&TAJ@h3jjz_Ze>J;o(fv&sO(@0XUwwP;rDo0D9s8QA_hAxN zAvalqI*Y>K*Vny|B0^eJFFlWaZd$>9zNJ&$>=q&^x8Gw{jfRI-o#E zWOVS~^}6s0Hj5Md74-Xg%#@cp0)qpnUZpU`p&PDa zJXMg`CopXfTbsdfk<8EoWY?Vv5y-KJwNgpAG~y_l{f*zV3HE_SDBS5HCNLxH#8xe_ zY>i(ixYbDnbDG!0vG4XaBYW=0)D*>GQjr zO4_z!==oY7HZ8q$7*N^}k|N>U7IiD@Cb9938%`6g0akUufpogdN5n*>eY0AEql>ci zzIkS`7T4k}!91n4Nxl7pA054o*=g<0RH))#EVu$wDr9oRYB(!#_QK|;{WXEcY0v#Er1?zljFmq7ib?@H=153XM&BhK45yHJMR&DhlOOi??xpzW zR4CPoe2m69kl9fHh%E#{=pB{AaklOez=ZQAf0eM)QH*3Ou+6^2yw@|5j#y?LUl|8U z!_KpdZA(JH(K!NZVng#3QmdOQC?6|=>ZyQu4mf71BUHRl82yE^i8N6)iQjTNxqp1;S7H}P7K2wMJ(#HRwj352#@R9*7x@Lo$;YZ!6G zccCy>`m7OGEAgjklQ|~Ec~Z*Eq47Wz1>HV8d1uSsv2=a5jO^AM-r`njl#KePqIIA9 z3oo!wfd&5Q+8QiiLCM`=)%$6T2>j?l4Rp)b2vW5<*{K;YfdYbNslaQ}2YIWr>`(6b zNj_Kw42BwJ6i%Mu)g4-V3H+Xzc|QiHwgk|apZ=uAr-NcP(H@YwVhX283sncF>a?z! zbv4b+N}iHJPDw9O8*$L_$b1;;w;y_0Mzcs{gzm|?$*w=yg~%yF#{7t6i(A^jm_QxR ztvL4#E1f2{bPONnj%6kB1Pt=}OtJ035_-~}-3pN^grn4X=c*@8qq#VCJFZr9M7cfe z^Jte+?IcprUQL?4SbMhfa9r|N&kIAeb1+T96p%O@_b+8`mZW%1kelR<8OF;=KUlz3 zu@RJELT zvb!*(C2Dk_ks#Rj%rCS8GXt(plh|hJ9ssc~YI>6U85WP*b3Sf%?BZu^D_>TFoqFaY zJ^A-Y`~NvJ{MN_zY^0i+UBnU3*YO+XB@+v*RoFhZ=g9@eY8{`I;2$-5k>Gzzmrnk9 z@R~H)CHIE+$Rvw9QImYrKj3UL#`nQ7k>{-4wu6uqm0}T=3aK*qOl}gR zH6=UXN0ak$xy|uV!x)U5CuB3;B4nf>g-xAO{!~QMa?k4U|lSAp?Lp4F3 z1lh2tq)D8LeE)vjux7Mu)e#d-90#2zT3)O-RBN^SkFxK>SnywXNL|N^SBIlAQq0X?&5p~%D1osr>evIQ9|=&zt9sUyacw^DdZQ*wgYE@ zo|__xj=iOE_V5v!beud^ru8j`{-;;#DS+k30Iht=QU@sE=?N9?ijTk;+gaa%Evbhb z@N-gEl#@=@iD7iL>7@>llVZ&qmy7w7&M?0`J&?L!3nUf$K1vLJgx9q4L4abYQjKuN z-iH--jnd->$U+p3xSQDTPFWN$t6GH|9pQWe>oP~m0GaY#;-*gWTFC zt2>}^XywrFQ{alhw>so5Xm4{qM`zZWA!JmEeYY>gLcYK^XoxQ>ftcwiQBbXLjy)I7 zTEkaLx5Mm>0v6;~k{ZLScar4i0i1BE@2i0?fDhTq#= zNVIH9^!K-@&WK}@)eL;GfaA*0#K`_6D`O-TBy4F)xMNMCF@pX>3SCUuC7}%zmGKCD zGEH~*WsU&lf^(C{`K(#f{{56NIR*F1clWM+dQxx3R$@j8pH>x3oL}Ns#n|yN9UOJT zMyyKGxfMumZ0bMge~a>bd(!g_xI*!|l=IA{3UTi%9Hk3&jtM2)0;`y!?#m-aMd!!0Bll<><;9g<5XI5W;CN6nrq7Wk>N6O2cS@q1!b#)QdRMhe zK52Jkz~CM_Gf%~*c->R-;}4OtqFN-rS`IVIdoRJ6A#M26Gx3&Gkg`2IQ8XBFc|`m` z*e-=E(^9bv3mrk43~uWlI%%g+h0EJbFE#O}rpw*0{H!a)_n2nfcJZsYtyRR;aPebB zPtFkntM_5QV9qLbJUEfhg;Vl9xlu(=lfp#w&OFm? z=Of5!`}WCC938MA%wP|{>d#pn=l}+ug0|aIfTb@2{n4ckyWA_KJYiWMje?Olx+bTxb?|H{cx}G4lGW;$q2Ct8S5|PJ&GM zmT7g?@Jydy>$b0h_J}FOS6^)+8z1Fo9#-R)%VofQ)FSd_o8yw@SoDb_8!j-(`n zRXdVC>9CEgsfgUwwyyrm~ru^X~yyzt+!4{QBIPX1Q>7FV5*8 z2wB-gt#nP!(rx>ia<~f2L48w|zq~U|lF3}Gd)3c_I&H1Ela6JlyVO z*1j7HxouR;k+FIo5Rvu3+_3=!HUqz86}L9lsByIN6E>$p`$4K##~YdW2dMK5NZ3Pc z2-F@%=32WJh&5KHO(S_Y)ryZ*?t{xv;E!6(Hm8DK%%L}qYROLn0?NAs9^tnM4cTqbwvwW@W+F+EJ z`eQi)>uI&&WVxfCBxmpW({v}5BVA#Zi^VA5&m|b>hF$5e_7}^x+D4M! z%6IqcodYieJevZwIh)^~0AW*pC9M*vHB+x5gbT!pG3uO7jpr@dJ31A+GBj+pl)aHx zb?a^>jo_O>tn9}AW>U$Jd(AdY0;>2(_ zWXF2scOcPTJvroX#T|1(Quwc|gv&lo;hfGLmst{nm>NLi!q%)B#TkFGpON9szEJAf zwn@@}7wDyo(iI4#O~^<)dFXXvHdioZ;q?s;9OXDH^wI+ab7v$2kQ^p+4fpD}k?66wN)RE* z+Y4Ls){|6utT;^{y$m-V5sk6Jyb*y`I92poE9C6kVD}WKsP9V||D?ZFoAN}($;7GW z+MD9&AYD^7Z*e%e#zZi!qeX4dg1Wvvfg|g_?CbvVt!{F)Wj=F}-aKXl!u0*;Dp{N* zb0?B4xQQdy+wbo;bFHdoCEJt8jHK3nGD-IuV3(g~ST!Q~Me09n5RV6;9X313Y@B7q zseH)cE>f?mUji&*;fT@k0zVE=PEcwi04vJmc+|s`&ZnDxUp*68^f0Y-Il{qQsN)O` zj_jSBg_J(R^ESPHuKn!R?2i67w<1$)=aFY-zhF#d2Q{N+4Lq?S24PCGt0y&CkE!-^ z3V|<^rWF|7Cccp4~3&Vjns1X@%f1aI)LC2F)etge)#H9nUU!#cx!v*5Tu_h0QE+cqA} zB`p%X&H1J$e=WZ`aH|pphxg(u7m&(6FXUGtK6vlZ3E>}{#6*pF$xdmz$DrONN2GJ# z=0BMJXbGg}1&r%}m2^6`jdoXfdAL9ncS>efVz?8oNK-{Qu82I5FihSOGUWrM?Nrq^ z&H`0tjJ6<9apW%rkU>Qz-UVmkXs(8f)TIbca9UG$62}7c9&6rwfsLftGN>mrULl^a zelAk>h&+auz%eUS?Z%mEGDo3FG0c40uLFFVPq!}RT6_d?>1FwB{4bc(dc)W(9msX# z$nChfbe**DNi~rQ0<#7lmPV`Uf&Kzh^0A~?s(#I_(T)+vvH*8?Ae3z++DO@|Y_wFB zuEc~o7zZoVn79B3OqMalDv}avHvB}?zE8I6CSe$-Z+g8dQFBbQ-9eXfkl|NKzaLYX zCYz-=>oH}nm_QEqci16H95&~P$*l`Un!L}#Gs0hnr%~mHRp8;x9+2KEq`Y|(``PSE zRnKD1+@y&oPwKIa9N>2nv3RvfLSI8_zO|^!WX?NnR2edHTCQdQ* zC$9&R5ex%{=gw~q2HIEL^;-KRD@Q%F`8SM4o0_%K@~fGVP~ydFkRs#b#^8Y``HQ!b$PGIFharzN z`h>|q#+@R709mrM(q`cjSOU!e1RNv%X2`?itKsx`47MYz^V%&Yp6R;nxhkX&4AKUP z(+#5SCIcsm`nD_rwqbO$C@tKp-0bq-O-TA==8+TS^@dsB0DnHNX$(@m`U45+P-$Kz z*@t}3(w9A7tJLKNF``D4gA~$&s7TQ6ur0U1c}JQ40KhB3T>eRl7Yt$pk|77o4po`W zEuKR1lj*~m*{inz{sN??VULi0T?q3vP5TQg2?LqCA5_crSH;`2+LFPhXUWCSa5d>F znJ`_ed7F#4EFhU6tIExUonFZ$jCB>-(bng`_x8$d1DG>EK~b-zc(}`$>lj_{s(6C6 z)pTDKi#>v8&#YaKr)l?RAe~Sthf@x3<3Rht6Y=9B$7#B_G$)48FLiVADUnamKcJx_4n!3f(7MczG}Xv)89ldsswWz$NxMN5`~}lU@)^yW+?hM3#}`HF zn*ibdzG3s&im#NUJBR{&={NC~B`}UZOJ2~Iy|D64Ks}v7#JpBHQbred%nWlG@0vFK zE#t~N5Nmul^Ip21DZ6K~17A_CL-un(=lROd1zfrk`wC39en98E4ilk-YZ{!(^+3y8 zoQ#p2nYe`T?4Ki&^gUa_y{%u1BP0#ud-swE6_eC16DYbTS7+MJIoaZSMGqn455de? z^PD2?nv-XBL`?VqH0zH?C$(e;L2FKVbvH+6X1sB?xm7LbC%DFD>PN62gT>mYy>Q@D zJu5FQBL%H7FP|Ak+;RoP6uI~n)PwrRe%(=Y{fVv#8`fERBDu1JGx*CTPogmUILw-D zPW>%Ji@@AFBz`N|5XVz}1$W^W)4kzypY~{|n8*SlN%D<^dR&G}Eh|CKHBlc0Xzer)3{{PTso0!2iD|7NHTKSiUR z{rS>1?RB zowhFj{Mg2SlW%U0q@2_6?eJK9LrCLMS3Uz$`$g!Bx}0FRefO!_LswgVzq%~> zw+VRDra=snXadHlnt26^%_edOo;Mwlq#%cXYliqNt@4e24ni~j5e>W(Wfir0toLRJ zI2PhYAdfxr|ktpW5~f&Ri2CD<*Y46Ou39%2>D|U$o%%P3;Ne?A+k9H}$hVSak(GuzN&AeL-bWhP_i;Y!OMA?Z!-5JK z?|#T7A5erEjVMtV6mRUExL4{^fgOzfO+sxcvH&9GQaAMJ-vSuft#0#3ad?-HJPTHw zJOdb-wF|qosbiue4$M0UoccNgZaAMW%1N_DklssOou-C4ttmYGuo#F>q|BhOt@hWb z`6(K8eQsa9V+s_~&dmI}+s)WxdZ&Pr{rE9X;w+&QbRr|jfcrfHTy5slHk*PCUS9EE z`zC=J?rVjRgVEUwElP*^pgFRe`$MJUCtXrw@Xf7eBO?Y#>IszScHi_rHXWIazg&L| zzMq0YcP|^98JhCZliXxFTm@>9Y#qb6NfKb9$HxyL{bkl{=kqg(*LepMuA|w5pD%M} z@4MK)g-!sam(brqoX3!L7ZluA*@w}?L%NM}WjrPjCWK~*ss}dD+Lq7j(VyU*$cG5` zSk4kD6lR%eURfbvRFEz;gy{4hnEiut6bsja)7ZuAzSx{+DW_Vsk*Cs=!f%0R_6dp; zBXm)AC3-cFQ%uJ!WQL-zIMQ$7w?DgLRcCZ z2`8V+ZMRa(&V@xqjYGO!|3d|K(u4a{yB49LGf0;^9|nTLR6?m)l5?xGz%$a4)ygzg zCl>#UhgSTJ?<8sWW4}Eh&2)lAY&Wgq`B40mQ&>7X(@)fK_?v++*$U_7c}{tS)xDhe z@4cW*g)+Mc*d|TmkWkfhY8PzEWOs<$S;zopN2o8N<3_w)br(YDkX-GnXExniO@Oa`^+lc za2W~zxarkp>hI%azMfwvefqU?&xV-e+)b-oTNc%c&*S;p{{v8ZCn#1tk;Jhg)3{Qd|Q#{-Z&F;&1DvWai=W} zL_DBfXip7~XefkH1=U;h`=Q&SS(Y*LN-lWns{fE;e)2pLpbUQd8L zg$4N>sXfd#KMwq&*Ypm9V#ar3=$DWxUF@nFd(GH{zVgdz`dw0U_az{)uLmr=_{-aR z<~AeAZVtSms!WhT{D7s z&0m0;G*}bkt$Q$E)AyMU$C-y$vnP%TNgvo_Mw07F_R=~UiUy}M960%k-xXiPCNvDU zfjV~Js9IFP{UC2b@`pV}6?iu0GR?qshO^xkD@k50)|8!I0n!8a&LAUuob8Idfc;47 z{V@KF^yUydWg%tK4%{`EU0kv@X7;J=g(Bvht9!QGVPuNwzuLOsUT#Z%ND4W&K5r$= zP>w6I@9%4ik%*76EY;Ok+|3Y^yr9L*PRCxLK}TUy(u(GOOk0QCfjnoOSU%Zguj#HF zs(xu40+KV22+{L<$(k=9*_9Z=;bkfnf0GZlu9`<^4lb9#oV@2ZIq;t6PO=( zcC0ek1@{VGZ_k}@@@&h&dQ01X`?Lqx{mO?`^>&lbnpo+5K8!bF8AkdLKw#LOl zT1i$FHwvT~$g3}1O?K9f9qZOgS8h+N7U^&Q8nov~H$3fEzx%lRl);eGW|~2*>To(u zr>?QW`j>{}%rdIJ6!#tZ;E9aZcxGHd zP@%j#1QW2F)=cvNYEqDQZ!k6#=b*senrMHUKiL)IoUR&7Rg_mj3G{mc3!2u&b!i?Lgt>KvmHh=$G?r|&@zzFMJa&x9eh<-CIcbc)iT6HA1}lXCkgbl*I%&)P z`_1qzR@-o=cBByWC_CxSa%<7C(eW#gw$W`atLOMsUtr45=W7pDjj==~CW1U46!1mc z_t@hFTpOW43d3XysQ>5|HB!MPeKWTdcH=k~I8Z-4;1|m{_lap?1cDg+&sOtY10EkG zfK*X6OTNt2oiV*>qy+JNpj2;JCK6VzTY9?TjNfZyc>1Wcg7}2cE(g4Me--GYItt`e zFa$RwXd)E8cK2x&&+L0LvZhNqzVS@mFa1-M8N6X+pywGI3_E*|q*)Dm-0RHy=bq|l zh#z(3IZbTMtiJ1gIp@`DDA?E}zhUylpIU|){6ck`PrY13(s6s4_`&G?`1*Hl9%hN(ZK6ccz-WINq?v4v> zX53BlLxC>Z%z2Hwqr%&vX0~eqvxH4ah{yxpiNVYopvAmX+C=SgFp5*CWO zySYxI+Hhjp2eT4srZG%z5mUpqXSzLVxxbsEV*1|&r*C9Fjz~}3t(;i|i)d7IFqHT8 z1FiJ)B7zUTA6mS3wyLXQfoziW8m~dnGN`YBNS3C)NB(c{xu;w)5(u0(0KD@?A2hr5(A{4rQ z0Mt;5IC@yd|CI{N{|g_)LF`t37%~tOgc5sG`yF*B6_I1OO}Bb!R(WOO4&y;B3k9YF z7gF6X-bnc>8S1=#O+kM5(ztN*Ns9-M`E=e!f_o%2pL|72B*WcDMM-kJ#UaQ^a20eP z?Vu?6tqL7Hv@-ZS(%f(3hW}tBnyoO-DZJ6ye1s#WGr%4~S6-5ZXSz|=uoJVZu9wo~ zKx7FDh(+kP|4Detq#)~A`7FLML6H9Xh~qLacRk=AvnA0}2!MtF)HP}HPl7VccAf+y z*dY+x)fZg9K|67rZ!p1L$LztQ;g}~+XaZ2dbY9>(g?knPb#4jLP*n+Ryf0bX^M5EI zBgOlTTz+4*V7D)@_z2FRf#uDC4+4Pi%ZmhuqK~SoE3%h5F z3A(GFpy_{*;rT3_dB)$6xRo}oNA5U{;)9{oygsHZKq8PnNPmHuaXqNE#tEXLxdmg< zZC}Hn_fe^a=MDM5W4Ili)H>Vu6ZyHjbqerWZ0_%Le2++ECSRL(V+jt=&wY_>eU`&Y zpTV4`BJn@`*l^&&k7~Kghf^q@OB9*qB|LL za)Aj(kf=Z_IjtryL;-~qe_d;~ar=M_*@^Vj}bHH3WcU# znL${gtMFRHO+AXKb2{k8{I7iNxye9J0YPl6oc2x;%7XuV_5k4-_9`eh9J;G8{=0S66`5Y3gI`@aNwWshS4PBaIxM*E zBi)1R>taa770hli>)M_9$gGM9!dkVIeI{uXf)ONRcrC=Jr-cN}Y2glb7C9u*VY!jz zj&6UVmN80j{meq%dJ6(#w??e$d&$Sm8=3QvJwcrOtk2B-J;$dk+tMuE6jf%4G@f`b zz3sz*dr(J_50Acsw=tP4_5+&9&X*wyT*(kavUO2g;0s&{nz>JdC1+xsTD;$x66X|X zI!3o##_%j%j%_{G#J@d*9a=utZncXW8xOU~TQSMvBd|9%M#BjWim0hz>HE>{QQ-I+ z3L5nOqdL@kFDJh%L+&oNixUR9r{j(>CC8j|xMKgQ7>=ViTi96}@BrXy$sZ(dLfJ%T zfxjvB0i--R(;{gUV1X~GxTh6KvJwISuvyf#x3d~C^D#yinVrNA7jCUD%4I#>YKfkeMtR= zf@D=C;)mL@3|mt5vkdyBmq(I%_$x*#wqQZePd~(S3^tJ9=2M^NSw>m4yGA56w<{0F zH>5O@zB4Idp${P#wXZ-PW+I1OoDS&h-Q{p;8^^!w0-qDa)y*|2Aa^5*rm#}aa};!i zFUmsC>lL|WP=>5Rx_nWjAcQQ+>jd^ovRnE-!xy23 z&RFlf+^{P6yloDed(B7EaWEZg%!5a0IH!2%sT1iof~juJBi(!pN{3;w(LB0~PGcQp zY+b2AWmod1_i&eOXPLGm zSz5W7{5DveUY3_Pd2Q7hF5DuFth*l2@e^d`Sc&P@GS@+i&vHT&0u2h9ym?iUymStq zuhXg*LT~!}S$SDN-M}*n7z6u>h}Q zvEx1Gr+R7+h{`Q4|Lrfvbgk*unx?{g7zMBNJ6x zC3+SJHg+2cyz;}T0*;pV;gx1-R=igGa9&Mbla)~HY}UuqFpk5 zMAOH;hP>a4(?R)H$zD%ST@7IHPjOVJy~qr2iTIK4@8pEXehyY@lLIJymMRp#VElbE zcB;gg*f3ILEskw>s^B%zhNz1|=Sd^$-sy3%xn-@B-ypw^k>kSTioB#;m%(tPoH5{a z7mO(Jjn+-uLYz2I<2CxFy;TDZLR$f7#fFX2Q$lsB7;!U-peb|3ZT@R($7xDAsdk6# zTPLp_f3)4BXW~Zkd*fw$i|>KD(OWRJD_)^@l)0mLR$}g`EPzXXo$o%bc6=vQ2B7U9 zf$iy>esoKZupa33>HQTl^A4kVRm8kN?QMDdDL=_<4|tQ&JmXSSQJg%!m-w_7svxZ= zN$tc*S{wNWnv5ov3pGX#KsjQQqxm^<81jq6wGgGQQ)gSPIl_VL*qV?HwV7PO9lCT5Q z;~n20Us`azkvhqwPz11>(BGu#J6m>v+wn#1!<1L=5$k4dg!0{Tbka1cj+BPFJZD}X z`BGjgfwBw5NBKGuXWbyYN-LrYg<7s096#{-;e=b1ipNjl$UF?jzI0YFcEq3(V3H{q z=NMx2O@)bR6yUtliM+bzaAFY@l81+ua(pAUU_s}X19x(DIk;5}iko*Tcs7@*?oK&L zOgP_3ruRFJKkJ(+o7IpQKGNhoZWJWi`k1%A1-kA`Zo0$XnE8Kh*AnN)5s8E{6&YRC z9T_3gMQ%_8n+WV`f`yK+0o+@x@4sY|54aM|Lp@e1iCS~0y$x4KL2xfp%23fs(V z-#C2O%diOwCw@K_OtIqM8NEG+? zWw`&+^)Gmo{tyU<(+1~#!@LcWIe)#b+qI_+>|%EVf|_>)LC6_%cU|oEj=}RA2kd) zJz!kFRuzLJXb4b8n4B$?FjFXkFdi0V%T@RA_!Y=sDnjv4QKajZG5>xOvPF`(svD5O zB9_yJtR`lV_go0tpNn)?=Zu8pA;gMell?mt?$3`+|4?{!15QcX>bpCpAacwJxa;^X zn6Ezl&PO*m4RWcb;61p2&7o`~uuXBl>9w=!)@E=Znj&3Caeu)@A>;AJk^+r?zmbGj z5S!&<1Ka?`vgKHGWT_xbA7p?D9KfLPN6$V7Nn{s0=;fwBhJOpvvXTW%itJIIqR9pq z19)FC0IJegqK5!2a*o3iZ$Wv&I5fzJqQ)yhfJfc1L^{v>Vu4bnYz^U(x9&h(S%>+b z$Y9t~pRN%a6K=14{QfqA^LvBLFVFV;o0kO4@{TtJsT5QF!sKLVui&+>foHW^7_!ow z;M96i`s0IuDNrb18QX*6GuuZfMoIvZm~5mkx-3BlW79+1O874(ap58`hb2c{=u~vI zZb0p|5lsoe{*EE6KGfYV?~gtyLJk%rjUZ{Nix>s)_kkW$ifWTx(^5iVps@tp``uRl zzJwiTn7bm6+2mQ%T& zK{-1JelC{!k61A6xWq}gP>EJO@ze`lO7H$KTN}U1;*<7t1dgry?Y!}wQt_KW)tT8-c0T)sG%==N zt9MeWdM4(fTS{eP9fMs9g|#}+VVGX5G~o@>At@t`zI_7DeB^b^F|YK7?%R3lMGa&A zDHmdaxNzURzRq`2OI$`o)GXEto-|heOEL465V`CC_bxf z-5<>$%)lNlVVe4WL4!sVG7FCA4^tZ%kj0SvVl;p7Qluqdl|wWtA@+piK8^u*67s78&rzg`$0 z&`;wXFOy(p-a2{ws(X%EQoH*M)O{15vr~&EW_$;Cb#2% zrhrVn1UR#Ko1@a}XFYY73zEr^)7L{@RrtTZ1u5`kDje#X+ z0b=9+^XJ`j3`=jW%E=fnrW;ED+P3(eMqXeE)bC)UK!9Ehf?}bfVH5O>K+vq8M=%23 zFV%gV7FPP_j)@FD6cjollAI6M+=5?>kk4tRx{~Pi?>$p?UFU`D=3%SW zx-Y)!_@htZ;+4W~MspHmJ8SC%fP{;^o&KvKlw3G<^ith+h7P>c46iXY3sHsYwZ=>T ze)u-IeahJ@*l7bFqw2Ba22-J~1pAO}VYgftJ2ne9BK_|hc=*@51>Z3K*T|_+G~5ji zD^C4aN}rk>%b}i0$;3YrgroO|C+_LJyLJ%^d|$@SZd}-FgUBL#w%h&=id&&a}&$MI4gh+is3`Ykx41zlNgJtcFZ0>|%?T9lwe zOWLJf9KWxqY+0U^#iN6W;1vL_3^pmWQX!N1@5Q}o<~Asc6&^TM2!$Nqc&!P;xtdU5 z_nM&~se^4vGCK4g)KRr4cFQ)meBl;g^%QH!*%i=9y=M)h9jsyNmI|bPbc1eUBN&;% z#S}tR3a&niC3NP!0XXA5YM;>bm*`M?Okp_yA}Er13a$vl(8iMU&AOkqMGWadAR%k# zl(@iIDL9vt-;&y`=V*-&+aQJ2WxSAuctm>n&AhjF__+PzaL$tYqn-=;UU(>;;yGFz zKWBZ?`M9AJ3X+R7;BX(|8h_VY=H_^E|3NKLCOSMLcI2==oLm;R?gfOYM)=BE5JDhc zxhN?d%O4%@t9NV}shpCcJc3Z+IuC*T@Rsc=zff%KI zm`)oYN6Y7$)fSBGydGHDj{P{&?eF3>wY6gA&WimI|6Tt&OWh~X%PD2?blT_Q2FXo-uf5q`uT%D z?v(m;x){X{pn-1lQ$1bry@ht>?FE)P zu`CSz9$=9yLMcU}(7DGKT2yUWgpo+aK>U4}ax<bi?jD1y!*# z3eE%L<*k1tH|RMluYhXKvP(ZrgB>8K#T2&F;>;lE7(nd6BIFp-#InpQGf!ty*Y57q zu-|X)`4VNuew>A9wa3mANli;H;1efz=@Di@_*fjg?|2DR{;3qCaLOWtLol|JOv%ig zR298&nMJSYPF9h3%tA)30g<2GQoLGT7DISmbA@nFEzD6zJnF!WhG#L(W@84KrcXsI zuMn7wGS2Sl@5=OQrG`TQ15op?7qo^eKRbNjOG}>2r`;TBBssH_pXO52P;`16fZ6kX z_UlEK?8`GvAQIarnWy%XdI&5fDCZpoE*E^LqS=*G5tR=3Kxy3mz3#SmD~0dUn?G+5 zpR7Yb2!TVb;QGylr}qM>TD8N_Q5JIAFU?lv4ukTk&?|(ebMdYHjL98ZfHJ37EBH0* zzv#?+Z}9PMdo{i9SCV5Mh=i%VSdINA_UEVHomjpd+Ykeu zgU*-Jzig+!4?sLtJi}TA=zcmq4@b`3QS1Mgz{lL{`JGh)gz|TqS1Cb<&va3ILx1qZ zHdy=UA+H_4N!D0sxG&$5HIE-<;mBFC?7CpclJmfhC5L!cYB_S{YEP=g#uGEhu)LE9 zorKYF2KGL4&K|l$;6qW)JVXvRB{J2j#Z|%nS?=##aHt2e%nnGFgkocIf$U>z)^7nL zJ0W62`R7t6RM>IzoeE?^d_@tQ^ftT+eG<-X{iV0QsnYM@WtxVAC2i$iG+5`<2p%8| zhAq#dfrcY+TiFTC!V^QWzo5q|<6Y*@81KDoIo_>r?19-cqV^YYA#*MNksTY|)m9QG zP0an`FpArbb`Doax=P}l<4qq}R3A3wmd9DMG1(rJ_H-?o)vnW=R-PqhiNqOVJ|Z!j zGXw8LGPqYaX3Np>X(r8%LL`JEBx(N|{sO@s1*E)rlX9h8c8K4NONt>mH|G_Yt;v4) zUKoD6A@NPKqFlqzOMVZFA<0HnyJ0qE_!W>P zloJ!;I8{yi55}oI7@0THfCkhAnrFY_(veef``vR;wl4#WTve?}Y&D`VKZ= zr(tt(9#6p4P9kSMj<=uz*}&pJ z;<|BV6WKC=%o^c54ZN+pN-$e(coBu1qI?`@+cC+-$^D~cDs;Gy$YDwFBIgYV{KaRI z=C;n_en`hb{l@EDd1jY_LqeC@@5ykk5;GTt{jU`WxmSidd#l1y0ixf_c;xBBX=}~N z^aaV)>6&c!uyE@F&Bt3rE0zNnL}{}k=5_rt%Oor|B0xNW=a6PROa zD(wEe5}q9Gy{a2a;Be_T13EUZ^E}IWmm&M$RHf7Ql;Mo-2{co!@NebqXqV)Md{_<@04N zBp=F5*Mtxzq`dMliL`GO&zAMaK5dK1?bi~y82mXyHiFEN4l;t9qK~zH?h;~({|U&G z=>u*>!Dk7w{#*9Ams3qQvk;J1g)pvjP9+jZ%dy?7;=-~>R|`*W@o8OlgpRh?DS`R_NgxS~6eZ~^nPl~aoUUbT_8c9^q z^_TC;?h6hOX)aEhY?3~Dx<0DGFzykS1D*tD*h3=YMQmb5B=sAQ8G3t&&OHclYnHY) zC7(>@KTW$4C$W4&p*_BOC;4ac@JO-M@p8TXopgtt;_k+~1Zy_R^_Gr2#jSujaJZP? zz;Zi((}Go{&V||wq}|zB60a~kh-&hEv((X_q@$20|d_oa$$KMTmo%I0S@6(>`FnM-YH zYxAkYOXDh^bUc235OcrOB0m0m;rLy~O4GjR-fc#Af781i;(K2-?cdH?+S{_t$?HMX zp0z|ZbIp;u`_m7Lmx<7 ziHwrHGBZmy$CfRW29dpY_9i>~y>9CFJip)j&->?l{&?~{k8#d@-{0@|x<1!u@IILR zwIN(^HZ5ouY7vdwB4R1x*rE49Qy@&xOutJ}fOp{;_I-lBMu&cmVhdPxhjlSi$x<`} z!jOO77=!D}g_gaMp&p6-osQ>0**>LaFDhlzqwn2&NAbBa2_{2{K0tcsy(9Ki;#exz zYc4}MR3It)_deOFcvMMx`F)gMPVN zX1qaYfqoWC2s_uy^gh`$r+{S3;=Mq7E5D)YoR2HA6;^BSu+;A#?%lr8_1pkE9ksP8 zJ0)(7uW54fjYXo)Byq5V#KTLCO{g;|%Y z$mM+z7fySeyfspL`}a-gf=hOQE@a6^=4p=J*Pyo*tBzxW{&&wzy82;zwcAL4JkEVru>AsuSLPU6{S=FxG_go2T?jEQNS{pxUu?BYBVD zXpOHW7g9GGn6UyQ!n$+pj;d|TNc(764oK#mW?{@mYcMxawpoBEPHT-=M)Crhd?sr4 z%?$!i;ss~LmPTFjK331`Yus^@raaeMH*fri8%tc(`kY{5nTGv}MbnUBQH(~l>r36# zq?O!$Lk-UFCBzt*ok?V}Hov3W9jx(4GiX$wbuw1F@#WyvnxyATH@CZZJ|VVRoidny zUOLpi8~mho`da62CB6q{k#Xlu4*S&L^lSJ%*I@o`s_Ww`6np>-V`!_^Ug}gEw&!Yz ze&O6^O9T_Fjk!qh<~w8Sz`egpvY0ke5&5YR_M76v-(kaI&yzW(K@ndSCUn{J+Gv8` z`BtnT6MBTd9D_3g+>@da=fpt{kBD>?-7nETHC(f6oh}5zL^JmLe+|eZt`bG`+IueM z@+Gl@jZcay4r@V7t|G!2&pg(Tpu=F)Q~`KoIqpD%*JS^NlMPAZ*oMS0(7HLjKxllSHOhGC{w zf>z|-j&qcpqGV`BUFhM35+%~m(V?f^4ru_RT5WJ3x+w?X0~I$!ns7s~5I+@wBvbE&E3Z&@C7TVGwE7(+rOjU>DMp!O3$+G=*FEQQ<#$ipZR(Q?PDOZu-#1*C+ zZ9^5J!|lX5zEgrXIo?OST{=?CB#Q>Q8YGUy?XniGO{5>Vqr!!LRzn3eQ?=XT?c$Q3 zqPuD|K82YZy!l05xbhTiM-9m=p;opOPv!O&>8#d8o7ew8I#2I4Fk@xGr$??BnZQ@6{&p$f;Q5 z>xOnSlZG9_kD?;v3S@zOPE%}<94mTP1A`+@!e4k3@7fGMKEa*j+o7)WN$(qB1~dpf z?HJl_{I&)teMdr{Re9HEn{{QGQd7GurTSqK@z@YY z5A%5VM~f}YhC?rDr5qfoH2YYS(0rTFa4K9$ZM~wAP;Jr?`5c9zFqckO)cq;%D)2jX zy}oS9DQ@~f>6o~~0AWRa_an(i#fP{>!7H!XWieMnPM4h3w*^?k_t?{TPKI_WrQh9x z&_<^be`FT0p+K(2RyRG(hdE{YGnL?D{xLyODtY!sbjk!Y=5vPuKxqFO8qf)+RzMY{ z4gQRlS08!GgqkK9&Il-y5G=ophj{e;tn^dKmRG^=YHwO<tmj#-V}Fi&f2cnX$HC5&*Lm-7LS4cv7~A!-{nQc&;u$?lenJ*w3|bfdSZtQ5dOqrT>B)crX<~-Q#EV$@d<;_w!#p3@hadR6Wk;c?93bTvIt$#5FvT5 zUF3mYwMs@8Od+<6AGkc0d~qjC@hJ7KV4f}CWqw=TI?W&dY{>XeU!f=quTFM-`%`3d=E~kv)I&7e6$CrU8*5)AnDvsTw)VQ5-JB&x5`YfcQ+VRJ z)8ijL*vUVe+W>R+>LM5H*oxn^3q8}_x3+_C}fn8cnk zON<1|K5i<}2bZa*SzG2|&*#Q;2kK<*xtB$U%^$5fRTaj93l-Ds8d)!1@kzr0xC&q` zdh0!)8eG}b(ukfVR>1>>`lHYX;06Opo39$2jtWk~=lpAqfi@|BMtZIH8A$FlpwKFS zn7T*pWSWecI9FX5E@U&sMi7g`v!+z>)hu@Y{l;?VEA%oQQBhLPNgPz3|A{NuKAP?0 zn8ISE_Xy7RxpEwiL0v#C3$Zty9{)6cUx4GouIj7bkku89qjoI==lCJ7uxGlxLPAuK8~6g&o{M&5m- z^zF;LPevm<_6e~`SoFtsPepC_2u+n7q#L*2=BEA4sPYnfQ#P{+ME+?c`qz+!#%JR@ym`AhIj z96E|&04DXnfVg~d;8pldtuC()B8=3s7@=p|R;sNz_$Pdh3y{dX7Dm$T#)DDg1I5LH za|<8k^Z7gElT1VeF3FoxmG`j}YvuEazh2k8&SwfvHb`FR*zq1x!C*A+Rw&}#oH@Y7 z`{M#l>4vlC?e#q!p2{@SvVB1TO-1GI;6B~YcX>f4(T2%u9V1IsASnVxR9AZKp?DHX zKZy5CywI|(xah`P0$z$N)pIjf47P8_r>~QjoL1pwCQ%O;jxTiBy?7Mq#%O7qKRs(Tm6{`Q(r5X@djq{{>FygU{QGpQ`kcvS+< zg^O+XSnZ+uJpZ7~Uq}oPoGsO5^bpv3_J4uEBz3p)F=!-gx= zC|2Mmv*CnMUZ$vlk{u<6P`tY|a*rKyhnjWE?J_DKQ2YT5v1G12?KP$XH}@)`tYxwR z2I$dt6rH5Jzh0bd!+tZ-1t`pNZoU5iF}`@xlD~`zWTSmoOoHiFOsjYtiyUbuF7+{3 z#y(hu1HJ-rvOU&D?53U3DrSKHs5i|c+~2QHjfKcd)h1a^bX%DXt?N_D+*t;4Z%!nANM| z2ozWZhoIt^(vW^MZ1pIf?>>A8`*m ze>hBkGA>lWeApO1?$>msC@sApy6^8%z|eilVlAN@)Sw&Gd}}9*(=N=C#Zw`hUN9#( zHJg0Kv$x0;!;Hirq%x^#oGaSDz>`Yv%OBLUr1Q$Hc~bPU;dwKLrdvqlU$gV9GbZAs zJ=(m=YBmqUh-8{sk|Xx@zalEqUqM`GmFbtj^bcCc&su(XkgFGjVlh!C!;z#R*h*>| zK;##?maq64*0&*VlR+mKIWDFK;R5x%%e?5$m%P3Uz;4SV^R@TX=#DD9P!9*^=Y5jZ zIxuySOK0cfkfSG?l!m)4j0Dncnhay)xju|qYJu?^r<2@Emc8jogdz$Zn(02wBF#zO zh<{GZO1yp-Of(iM&g$UfS^QaODa+XDOM?wrV)wkk{nYESFmq`WFaUd{nBq9F=U}oVO#p+2(d}lRoM=SujAb={4$9<7#L)$AvUiiYPP3+W zvDB2*Qy!?L+r9c!{@m-1*wDe$)nrPT3Oj$JDoViiisLVDjUy>B-Zr)JJqG+HNY|cchdR7ayGShkXHJ7dSLUk+7!dsxy)QbYt|(vA(B(FB#auBruKcMfmXa)mc6a$t6#C3hve3pV|Q{O(PQh zQCtQj`UJ6qz~)TvcF4Dnh>;!Mu{RkB;5LP{v03d7w=TA^XO>rYJ$_R8?(IUVYuK|EDXeNwccqQmWR;;trBl%! z!Cok`yVsouYxr4{lO4WUBhnp8h_+Gl_NiVej5hI#qH&>DC2sENjO$R%Kb zLckY=|MfAb12!W62B!g#^2p8QTg!Yhb<`%Bw{?gth*u4hx4brYd5=xG(?Zm(GqJ6h zY)(^`Mj`4pb4O&eL(Bu}9@}XiD=VGCy&SYkpRB56s+L7*xMe*Ki(NVXKVX z1B{LIyu@MEEuzp6L1G~~qgH^rQvt)Jw1smi)G6n|01#0;vG8BZz&854c71YkxM%&_ z;{s*O=hV3aY{khR#aa(DL269V4x#IMQ6k<@>Fw^BV)&}la%l)UJT5^Mr{jr^`SeU zrFz9%p#?eu#Bt-Mc2YejSrl zZ-Jy^WYk&)q=+)Om*p^|>NUWkwgDe4gZ=C42_CY(5|F4K9?A&mW(xVErw$D|uBRz} ze$*Voi0biZ3sywDa_5i(54xL$$eWQTmri@#!7SeN3LOA@oKY_@3YsR*HDB0diJv zIrOm?G`iGa)P?_)!SI z$LH=^D;>)Dff|G<=7kx5c0I5uMXW>)RY2p!sGfOV^k;_VGZv%k%WBo-l+DhU1{xmh zxAbW7sswD!UAj}Bc*2<2BwCYegzUe*>BX-8 z>vKKNN_3UKtXFG_NiLB8lzI36VuIEQZ%n@kj8jevcs`MHY4B}ir<_?26AY(1*$Qv` z*Ka#E&Wa-$a5dk6u^XNty{>l)2%d1oR;^;`a<;v{>h759j%}D$^^;6N)sH6n4!@5- zuRu*~+H+=x+c9=9Eu6opH9*;L>;E%(?mx85Bl_G4E=PhRPA~DH9ZOx{|J$6rH<%NRvo4Rvt^%)5K$bYa=)ExI9g_@6@%#tb+xCuiTw<%O8F!&Cxr6dMFA z&t1(h=*=n+Ld(Dgqs<=3?NAThuzZW;FoR8C*>iA}8i0|vyV`s&z`;{UEw2FOj5#~S zUw)Vf-eo4m>rsc+OM1_eNpPI79SccBaN)yxeh-^u4wSv1YyANmfF&O#f_8uMyY_rO|BxPN z8V%Xtm~6zog+)E@eKs-xwXSqpFF3Rae~E}=8J|h4po^f1xwD8A6|hledpaO&4UBmq zWwk=~0c2u41^~zP`;%!gp0YayKmSqn^2VOI(6tOGuz#TkR7sR_c^>@wCD zLn@U(ML>@M3!tO9KkU@jf0b!x6B6D!_ZUKe@*+Hl`7`Ro@3(-_XrVb01J7e$VLDMz zI-jhnmpXcI94d2}%y3&|-XE;?cQLcQ{u`!3&2d8!7>O2qL;8v66ohEG(RWFKhx(ys z3{WCU!3&4KG}7r3qka9 z!eh$-AeADT1~YB zV8_r=BoEz-4ooB4EMFI6`s2w-YwF=!u!>W6oE*M9@+U@l@Bz(xB;=z|qZf#YH zqf_&R_+N$eyd=f!Y)2Ks3#WBOYBx84+vgvDM7WDc9Q&b_v`vTO67%n0{3}o^&FNV^Bv$5~O;57W+#xsDCBFUp zi&(~rJOq(i_Nl0+f#Q;D4 zKbQNHEIWUzBShoIW?8(vbeAD;*X3XOx&(W{-T=(dFWzx3L#uo*L0pc*jh3 z);2+9X@hK+md}9W*Ivv@*mnlAt2hxIupvQ>pKSnd-5yXxXLttBP}l`q8USC#!c7=q zT_7wS?uFed^c8-gXh=+t#9v$C_T?)3*v(jRUOqf~Ol(qQKkB4kZ4nq({qP zJz+{k1si^$A9G*TZ(G?0rB9>4pgQ}2B)WJ88BVQd2$Co}f-AHIFJD)mq-CZk3uLuQ z*Y|+|bZ8lzXgc6fglNny++Z@1vH;u0#x+cS>+<=OU?0>_I-4B$jtv4)Anxh@>^c@N zX;pfM+DRc^$fa!~AFz2Y-Q0EoHZNsyDtWU_Z zc6XTyjHLMig`4kz$v&8otBw%oC>60#7`%<|qa@Hx?cP~vEuM+N@O#zE*B+H}B1b-Z zRlk&F9zb|wxBE!74{{zC#2~sqt}|P&vC`?QI&UY16p>h!5>Qc`YVMJs=tU$dm=Iu= zEN3Sqh~K?r@0Ten0eg9tfzIbJYwX$f%I6LxW}PT}y7Y1h8e)aksYKwCFJ5L)O52BE zD}ygm%uGr3%!i$l$u9Az(kF3>T_hz4Lx=m=|JkM*`lVPy%gRTX?|t?9(H6{Y)?kRx z&_vH9CIqtai-)6r3_Pi&ScHbM!1{}2$>8ODg3d13H;bPGMtRdDK~@~E*+Z39Swsx; z+-@iixFLki%q|cLe~-0SzMZIws*mAqJl^OvP#JO|jl-GOi=!pRkYM`sJUl~#m&5(k zrd<*7VHcrn0*PnJ?rnLP4SlfxBNCV<@}}?Zl+>Rxb_m}=v^*t1fhMPveHkX*@n8gk zf4dQdTfZVYWO{&t+bWJ90C&Xy>B`T&*C;9Gx}vvY)=Qyo8gus{Dn0Mu4EQ ztD}qT%`8F~*$K%RYk8`8RiQxu64u?Nk{W^x2(G&+wV?mni#A^9&8A8n$G|cBVd?4G zAV#(ILeARY=MX7M(w{$`qwmE2%^kNRwoX^gj;Yw5&2+#Q_2Jv=EX&n@y{3(+SQgu1 zGWo?{MnZ?ELFBzv>O@A!Jh>71z|X*V)B~og7c-RoCz;PBS}I9X_i-7DM;Idmv-Vam zU(HhM1Wyui{dkiRGNoA5sicu`^baxOaK%4~z&GR?2ogQ{H))&<(PmRWBhGkncL*OE`3CeI6R zYDRWt*s^C%4|MK(c$Q}p0jDFdMyx#vR~Pjhh+3l6*J z8@g2jlfg!O3=91w!wZ_LmO9{QQjx2l)-m}mmE?s|sU*f>l%QMMiofgd)E`$PbJY+g zQ?c*MP)U2#yMW}OF3F+^cv%0&%Jnodr^P)y?rzdClCAF|UQKoY>ShZ~;;2^h&itGf zX&wyUp(DUt8QyHNCghW9gDlO0ulKhv-*1vprIx#2`v2(Cc`7SPQ-?l#C5jhm@{Eaq z5x{5X7ReC`OiinzYy!0>L;$=-l`;vWhWGK@=Gjzbq{-YmGY_U9&i_V16T8FKgr47_({cB*4){dhNyrfUyMFG>0 zN8Msi+sm?NWeT8li28hsqC`4@skQ14_7UEBL{5_`@tK`ql@4n`{J4~XoAT%cU7^K@ zt45f$PAvgC`W?H-@O*X6AiHHtb1}i2((HA{i3KPMEcjSyj^g*ErS9TVo%fLttCD{9 zLL}iN6_?VP3U{sqwABWTYVrV29b#9>5Ys32Vn?~4?JDrX2)LW3~@7@-QPFN}0 z$3KQK#$EWN3wG%^$HkjfFu~6<3Vgh_m0Bj_Y@70_t%#XgVC)=XQpZqeqwpxR*My3b zIE0BfK~{(qN*!g*XOl?bSDo1MDyVuxP2V>_oVT$3e2AHr&kD#h z=N1)00pjIasrnBV-Mg8PC(8suBIdc?Rdl)uq6h*+GWoRjwE~*Xy_x7RJVL2j(aT*Rvec4 z$h1)a=X4>JuObN{)ke!xBQH_BDw>cQ&JnSb5{a?`u2Wu%U??46!jp^ zfqxB0{E+Hi*SO}700q|>w?;&Q50LH_p`7!5**OZvi~1E(_fSxm;t%M?{XNeh1)2k# z#eL2gm$LOPtwF?SXyPI8(amDxeULJbRfl+pkl>6xq?=bjDGplV2z$hkkKSZme-HW$ z2ROwFrWnSM{?I6+Kytc(_%|s;V(DB0w=%^JKl)`v;$M{3m6VV{T<*hNnDqX&C^7bG z(kv73I(5sjT)v9y7ct#R{ieD_&7=?&7L}fOA9iX>VR4KyhgV6TG&PNI+|)pUJVi0fiag@f8PCLMS~tGn4+aG^Wd`Ihx?Ew0-0)3c+4dsdWZmlL!&j z)4cGU{eCrQv=_m0?+NV2B_dv4g)&^)M7}k+WGLV}D)muePJ}>b)O%4TIwj%NZi_bA zBW7olY;}k`Fc-V*TXPQ#DU0M8#Ugxnm(fu&mhT8L*-GiMqWs)05wwCZL96b!yV2@Z zf;Y%;^WIeC32M?o^h?H))3?wRk_lG`a8p@L5R7%I(4C76H4oB#Su9P%KOdcu7&vfx z*PtjFvnm}qo%zTX3X5+#(M;^Soau8C3DM6cY7GG0L-4(@pAP3Ui0>N>4*1}4IR+dk z$LJa$&t?TgVJm-TI9S23bjW0fW|K`a$AF=t`c@AZL#RvtLb^2IpPr)}lrUh))NF)a zd0`RExijk%Z+}L%&t9@uy%)@hSv`4RmR>3q4--XtWko$`VT-K@IKz7JxJEOdFmb%s zckWnTYUqPe=Ae>WM3_&@EOQ9f&rlpAPR!GmujC`zA6?fX@L)P0^X(F5YWa(>1f>07 zcTJwfBLL}JpjU7q=T@<;tZs}))9XnPtJr}$DMsE~HE#=+iX|`EhBP%D=0d$+^S54& z&kUgmU9Rr|CX6_yZCx3t{=ZuPbO{5Vun7@Yn(9*BDP9l#zpClgWAV5Ua~qv8zIgO! z*@&#s5S>~wk$ur4L^S-P=I2Q$?wQL&T(gASB> ziGy>SLvkHKXLrCVwe`d~UuB+eYS~KfQ$RY-MZxOAtOS5aY*#)hY)&{YiBiTAUj(Hq zw*b}zG{mA;_1k7UB%kmJ4ANI#yVE4v%8vL#_L9_;3?0@)x#Fp1Y!}ZU-s5p8M7oH1 zO9*=l=m>i4Xq-vjgYK+%g+@tHzB9QX)XQu&;;r5MUg;ufuS(F7FK*rQzuv)5lXQno zrcjYaL3iP_}AG#DQ;*CME!8JjST-L=kz!qD7NBUiu}0 z=RIeVaI})T^vmN=yv$wplwrdzCYsRz`Vc^>-sXCaQlJMBn?2P%{z}}tO!>JkxK^5e z(J+0BeVS+{l+X0%{fLiUfB?a8${waiwx8tY`1@4@h^u{SF%iJtB+ae;CKaI<#HvQb zB00|warNHbYZlzp?#nX>nU2&062ZNG2G8`wR>xGV7@qh9m)SjG4;8w;r@kC(nh57b zQXp+NPaFF*X(X{`hO^vtuj~!RPRB$V<+djbcT~w+P$l@@RA!{^zssWg_p-DpP58sH zR0=I)A5tfGm1Q?=Bh)Xr#Dj>>$>oPiwOgQfVM2gG3z zh#i*w>1?It2-nc-QyEwr4#2G4yt&&6Ip<>)&@iUbK>}ovN@tT(E;2|Bz!1wnDJ|=W zw-46=QjP3Xu>~XIo6Ai~os>A%92TKoH7ir@Rw#N<7;tN)T*L7w9E5sHUJWDNj_iM9 z2?hrT3xgM&<<}3+{Jzq#Yii;nGki1uwi}z_SEh#enP#-)9V4HNpK7YQsfBBtRdPn* zGSOAvbLEU=RpjoZFne)ueA}RBal8CRxP6U(saeo?v9iM9z~TdcZd$DiqUfI*Sl#Rs zoU$K^2f}5>qe{?>=A=HE@43?{*9udN-$0kRQa->Im>}kjeG&09%wnqdgK?%2M?9m( zKA^co85tSdQ4tNGzsK|#f+`1Fn0HN$ZRz_y%bZxU z1O&H(7Cgf%?0Tcy{wftoc$j{`IxTe;h#s&rktI<1sV{7bVtb^+R3Wqo(|}r~?N``v zxm%;Fk5Yfn-Af zR>w2w%btwa5e85SYBtVwhUzuKWIuR#4Gq(wP6;;+UC-z{2B#6Ac`w4HfHGyY87`)A ziD~?3XsTy2%}=;C4+-P)r9{c0pg;i(m#}#!1 z;UPQ2kKliqfxTHU=#fI}s6S$S+fR4@J?N z*+)xf$O%!@dzh`q<}_GMx;mC;P2oQ9LdBq8URDn5457buwlby_6Gh5vW9SO+Tm4h% z^euTR4R0P<*-5EeJ@-8EX^F3|QtuhLrkK@&eldAFl0E~gk8UOe<^4@IZQ5i$Zx1P_ zjrk1J$!KrIWW-xot%2t530xL!%TmdXOtt$MKMk;n#_F|$j>Eq>BG}Wz%uq|Vl;+m0 zrpc$v%gajo1Q~{OFKG7`t$ zs&u*M70qj#+D9pAYg#~QSc#`0rQB%*G&gKaVC&Cti+I>iv^dXjS^xS((`~O2(i~w) zz2iI2I(e5;LsH`cQ5BhxbLmibGRngIXbHe=Q2+e>bDlc^^3dzBWa){e&^I2J;S7KY zYDVeo!<;^Jsk_0L|&h0h^T%`0>NcN=_iYGB*Uvl`^A1y{?@( z$pt@*{Jho1eDAC9aN^EEz-c(F2Gs2@A%E}i#Y=Sh-)+^FUWcczD}9y|$pmKQzjyTG z@So!*SM#Wk7A>3YNDa;|+#o<-&%p4`3uMqlFmJAfrYSwf=kMo&e{96h^;|ss6H3vL z7jqsl`jLy{Aw+bnc-?h#fZJ(Px&M2&K6T5t^=vB}+T(o>|EGEpz~4JRgoRy#h_Tmj zPSS925W}{wxOepbBj0q5@FZ9FhXK2<3Ri>jUbnAP9=%_+DcytZE0Bli*BHxf0QK$f z<>k9D$XIjuuOGbxKbjvvkgx7=9X|sF&)}`F2k(3amxq z40S&ft~jA|p}&`*>x)+QtI=EWQ*|jmq#?prkW0Zu>gGRW({OV(8oB1_Y=58@>a^8( z$4-yjYg7;%b{SK^+sZJOdIGsM96*z@hgPs3G5VjaB7@bZ+6-_qFG46QZ@j5fPK*Sz z%?dr8ziL%J!_c|F)Rm*7#fj=cGJL`P&1WBL!j=^v#$dH6r8i<*g$j`^Q7PV6 zaIBZuJ~MKa@}j$sk*~C%9lbWKO_1?*dzTwD?k6vWy zi&hK_+l~V><9&IWT(moM^yw|~j=?jsr3?ZB+Le?XCc$4k&Cg+I^uplf#%=keE|2ZJ zjy`39Ow1OV*W1D-Sb+N~_2V>RTAncfHT#gWuD8z5beq>Pu;Sc#OlN8yh+LolyryR% znBXGV4X7T6n*V#cAD6OcYT*U3gZ;gm+YNRpHh~-x_WhP6`cU?72M49ep0ZGg<9P`c zxQZKwgH-i9^*y2j4YtPI0WmHODyi!aUEb`=mexF1KlM;ZL0nmxC{#%2WnGAnwQbvQ z*Z5-Kw+>I{?aYEPe)%1Nt)IKK%BM;?hSABqOmq5jjbfPOxJLhrm0Pp4Tx}i^69rL} z7LG69o_q1t8i?2ABjbTq%iEnrgz`d?w4 zb-}N%uLtDf%9@%Y|BK{zz^e;CfVd{_{aYP*Y(au68Y|H&z1>VG0&f2Tj z$%y|8u@6$#waO_u7?i3d`p=eD1O}{M|qP z-A5%-6R=W!x-=AsJac*Sj)(!mOIYM%Ak^*@?!ANQdl7g_YshKtCeX@Qu{GaecpVZF zl2C0v11V@uk-iq;z?-}4W<-^0z>;{I>UKRyi zE?$PKsqgwg<3qiAwUOjjrP!VFihWTTsZlSE%AM_Bt*twUP(uHE@37mO zg?tcr-Z54eSXo*3!mh>Q^`J9V406O=i%yl(68iG7Jn)oNPHk}Dy``12y@s?#6-FZ8)8d5( z))w8hfAtSaEph$mE4ox{po$5ztsS{coitzMPxwL24LLym`$dunwB3;F4T!}zVa^@x zaywjKUtehF&qFQF-MIv_!mj{|qDz7=HUix#tGZmYZ@C#m+0t9BgqGMUu%_v!~%aT*P``2Y1B`uN~s(Z^%q0MrKpKacgV7elpNxE~=R z93LPe36z|tOU3JigI>q;++l?k1i2q)o`NOQt- zrZ2p3J}ADivPvhg-;-^wKQ+5WYxl8aNXqv1`PDaRbFBa8&!>OH#JmXP=w2H>*uR2| zdWUMoW1c#}0BU4r#Wd^al?tSnw{PsXKlKNnb@1GbFv-!RJ6z?mpIp)l{pHDjUjCyvvUNIYIb833pE?9vIucE*+cz8BaAV?0eQ%78JwcIy(UTB?5VZFw+2z71r0k{r@I4^JVU zgnK$+bM%I`^x9+Ra$7%KT+fpblQ*Knuk|xqVqT5n3MWk~@aZr8l2kS{b6eozn)Yph zz|_Q*Ho}VApKS(-Uk$I((a~!N*NkLV-~aE&48F$ES$)w6z3nm#g7qPk%Zcg|t#&(@ zysekR+L&%ny8tkh9}rlvuEiXdedr~WbgBkuMfM`-O*IkU?Pp*!i75$w-vrwV;!e2U zcbz&GAulAFVzFP@v@ZqXe5prikDYh;fVv^V*Vg8D7G?CvOf1@T$@i&~@$$rIs6;#Z zJl5zUL+oRJomQVArPXpPQUCvaztd^7S7F;QMzUP|vFvTRFq+*OA+rTfmd`-PyA9DP z@Lt50%xOkQaa()Z0U+M}9dH-hRcJM+iOGhsufLO}!el0dqFj!!HH<7E>I**%d-YXb#;L^DxHibTvtnfNynh+LJzlRik2A-QQ*PtS+unZ`{kT}iSKXL0xpgAzzG%VFP3^wt8cnZgpR9=1jf;%udb0C+ zo;qwd{_FCm-$47pwKqiESV>;o0lgiS`iDmZFl1+~ynk1@QSt;k>;vBbtxKxmk_frp z2)Zsmq{C<7t$=MvC-g!3o4hLCGUHo zdJ0_koFIKod`aa=vP?RZYooi4Nm+Q-z;rf%0qJ%o_A`w(JI(3ViEBAZaRP+5&2ecc zH^fH>83V(}oh2ppmshLuG}zPMvDO%S*Hm29zf>}$ug>$W$?jU9KdUIipO^N38;ooH zFtPQ57=Uwv&o{&^EV70o|2+$+SXq+15G)%qkvBo17#Yt%8i9U-yhMaIR8XTZ-)Yr7 zj&N(uG_e5SI#JcCUDswWxcV}(V00XB*w`RA@8NM#@9j?wjqCNs{AHht%D8u>B(F2* zvmU5pwwST3yDdKTUJCLM(Z{*R&yCmBg3cuyb zOjQ-5!yDb*2KIJ@moIxk65$w}gC?@uKYxTVcG^On z)wzn{4GXAKBCMOg;+dW=;h~OPn(1=m-J7V!3L=aIoP9L_Ut%`FixBI21@Ly(f1M(2SycOu`F&z8{q@gY%DPPf8rQx88BeIAhD)(2p` zzb=P~EPR2^K=qu(?F6Rq3AoZ-JKSMdkI*EozBwJsQ|ES zSCAyhu^bhX5yy}(A_P^TpDlNSejB+c-FPfL+QP~1fhu}>dLAF4$}F1I7^3?W8Woiu z^CDFCP8;Pe)WgJ-mnIAlm^^uXvHx79%Ud5D||IrDI zii4aE!7(h;_u)hsFk+g;#qHL|8K^V*Sf)t8IPuiu$B*|YpzX>5y%W~>s0mB-dsJ9M z+Zd?Q$>3>#`smAK>SPK;|Jj@qxCO-C9;%B~J607VB+>5*BUtaoCf41C_R63c?x@Cm z1%)B0p@yN;WTWiAd-CsJrB20>9ra~N!!}nm*J|-<-UMJE8~e& zAekpfe=?;M6)z%{P+yVxoY-;3`|rNO;#^lN?gUXa;7gcxD2py9CM6M4QU-x=?jj{6 z<;2i6;=|9WBoP{W1%M}C_`SNExz7RH_z3K)XJ8$Vb}KtR0;#B2Cu0+n^jXGcPt58r zNPvHXzg335fq}t#m727$<9)9X{S?z71OzY^S;%0 z^} zSza#k&H6q|yEBYR??48DXo;BV)ExM?{Q<}_>3f)qqF015Z#>Pa{@iK$Rm=6s*vHTW zvQag4IlQ==O8o^$U&?Pk+=+T@Yrs2B-R}k_m4DzoU#G@r>q@%eov&S>GZ7VL(ENaY zd}XNWa4!O2DGF}<_ugJoFlh7O>!n@@7zRtDZT@eoFpy0TyLp>jMZT>~yaG{e5?QVj_cF z228&)YEF{$5didP;yI&Am%m_I-4jebL!xZW?zlD}cB}UXdTAJ4e`b!<;SwILa+LI~Uuh{R z8L}6Y1x0IUlTdGCnp$>MVT7Ym2txXHpFEZt7EsqXT&8XvB95V?BxlTQX*8D&Jms4C6lA0?!cPJ zT>Sa-r|4t5kQp+4+_K|o-~)-(lKwRXH(ISzHZCp>{Y}fDl6+1M1$y+z>p>hlH%7u& z80HYf5A}?CXAt@s^x5y~#`N?giFDc#Z{KFSa}>NL-Fbt45MmVB3}h<(1pTzppOIOQ zaaCNP;zc<>tfZZu2v5%=2r4{ z5C=DHV}&${a)uCUFIzktg(;ErwoFR5-&tRu0?mVB>8m3U_Nb(fzjf}p{^^VX znvtu$!@Ik?9Up>c8HiLv7w9A8>EviXUp%^1`O;ynP?srV0_d(k%q>eVpwTDPU@T}# zPRxn34R#E7xqpI*-iU*MVT2mDN4l=Ar(~SnaHnoXrbkAUVFQ~>Y+RL0(*Uxk%%LzB zOF)z_?<6>{n3xZt^&Bg`HaF_I!YwW2y?CFDo*Eb! zXeltd*Lfc`EX}S!Lq{i$VP)&`mb+IKo9z1}Bga4i)s&^4FY5U)Ihq~U^T+bz8<8I7 z$&M?a4ND&}dQ0AEn-#IXafr9V?WvvoZF*@ntl|pmgBykC3gb?}ZK_^V6M>>={npX# zW2?j2haNA%VQI)I-jA9(2&$l&+??R|p4xvR@wbSgg7KF&>hi$MIlTPX{np$)X$+kfiJS-eOo(;(Io6T$uRLs$@%#u+-Fdv<8<({M!5t#mZqy^^ zwDf}t==zmVYlyp@Q+y_Egky>$la{wF1c4Y<%V%4W;(4=Z;KTpz2$9SaTRe{ZO%%%!DJb2JXXNVKhmWdD+K z4Eg;Xc>gNb^BxAbc%p+Yw(W)q+9*|ud7t0TbcgaO>D@$4rS>P*o6%~|=h9%s!Fnkp z{d)cL>5Lp2x*Jbmgq&%ml`QI&o143jJ=~WhQfJ1-lH4yUw;Doaz0I#CeO4^y1QC(R zZL8Ff-=61Kr|{_PXjav)Zk;m~s!zu%x7}8h`7tG9(lF%2!fZ;GmA9uRB-QouMm$=% z;0h6oS=D(;fpm8Twm7r{^DH=3vD#SR;@%>SuqETN@BxWPrd7s?RT$2sTj^IV7&>P> zrOomVTQEG8K&QyCN;l5K=FX|C)bOB)(w0as>|!LO;oVE9jr>_b=)oQ&WBy+ULq6a? zfpI1wpZvnPf_!`wEywT848kI+P){H)J6X>|O*8&AAbjslaZ$>im7{b?_$eVcu!V>_ zcqFw(Hm>TEnI>t~yWY!~$;-Rlsk#rdrjDJXvH=sQjpjN*LoxwW^%|rfszym;6^YXv zQPE2^Se+={Le^|35qqjr1j_4hm?r`mEDz&?myazPY*LdA% zYdzPkk<=_~0B!%zvvX&0zd)*&7iV^{}fC;*l)G92R9ipCr+GLdoXct(JmISThK=&Z8G6z7FL&(`HyaeWgk;(rBhXA$r0>Jp0b!qPgBEeL?`YI{x87MQRs*MyE!pDPiTU!@w&GrNzwA~sJqqDadBOS z6E~;)85CU39MG7$)rM{v%T8R0zAvV~q!ETdkQ)dH2%z0?`*(MS4wrqprgVRqh#KfmtxeP(XTAFd{+ zp5hri?o*cd1J<2ipjs2=)P?g5TaA7jV#(b2>c`X%UUGwqs0)nT5`C!JnNgB0MU`3U7cG0do#_*4Ga&^|1dZg(52TT?!co#u9Mf z>_2#1HU*-K#G#kWzTYI%1$=3=8 z-Wsw<(=xhz5264{L5W1)hJuXDXC#n~Ymkm{W&C_vO$}!o*FGeLB-GPug~{^S*htkY zH#kc>j`;~uo1ybY2CpK^7~L9BEurx_brxmidAW?~)|KQG>?xL>o=51j`D&y|Jna`T z8Sa7e+>$9V$XNa6>r<=&^<2OxQ;e|u8Jifo^cJv_%v3MvxP(5!yhmb4+aPzg5!&zs zRC8)d3a=dw`Klv(?kQS7eqBHVzQCTARn=@%3LDAnQjhU`dogy%8F*NS{Y{KQm>;_` zuXpLU*;Tz!_@iG6P215Q?g6urZ)If#y-wcS1nF#O{kWPsw#Ngdga{V{D|BO= z0cF~zwc!uS9vPP$j<@u4N?pdL;S(G5iGH8)4u|k|*?I1b>tiwKynF`6t?~1myvx*# z=Hp*I<19kB${S@qNqQAYDJ0h^@PphufQ(c2ZFtBfiMG_r++5cE^<1|RXrga&Xw!~c zG0jzs8a^<5_YX|KkEJ?1^AoYMsDO?v4Hf%)tJOA1?t*R3axyVzb7sfA2r@co?!F8N zI9HhqG~R{wifi%n+-;}A1@2v>VP{vtP;CLJvU5lIZI=L0F?c5dV9T&7W1d2q--_F8 zI<(Fsi;Fq$JO?-oc84NA{XcBIby(C}*tV^Rl1fQ;DHEXT=y3gynBOALJ#eAvD!E=9;$=1Up9~OIF8w(a`d!;}cNV{RUWz z`jqI3k{Hv2{5Q0#mP<~i{3e>j(ig-qus^RFB2WSR()H=uiVa5mh%jRrmO|3+l(h*3 zr8e^a6pm2UF+orYDspuP6CE-n(g|`Je7h48!*SAy#)&ot>0ANBbyZ#KfQdvtWjM-$ zP_q4V4EKtwe)o)tqfrju;$Hwl7b{etZF!-l2>pwgFOnl6);%7LPCGSPi39Mw@k#DP zzVr1%f#c>s*qonFX9@*ul=yv{lij%zrk}Dy{iAfDh)|i4=XXghT=Ljd>CJ$U$LG3} zjBXRJu@$AP35;j3>aB_c?Ky%3DM!ch8@Q2-Gl^$MOr${{cm~CFA*8ZpKTz9xfy=IAreAB!qLF)AdkOGguJxUR`~(HX~N7>IZ7%VbaIwYq8m9 zEcqaVYv%5VG!r zM`W5CIm#y9CqOlEmQ%AkNH`JW!S#*)$bQg-9EaeAnw|Wi4>(4*5!>iRj6s-ljEQ6F z4^(-XZ?a-M<;;%qM5_jv8p;R zrnLVQK1|_2i9QE6>eAPJBruP0Dn`kyj8JG?KE#pn(|L*kP#_r*wVIe*+COWEb;+o1 zpf?mc9QoRL*|!#YKMI}&GRei<+-m9~?Pj+6kL6qLfSg}t1xXou1JKh%#lTR*7c-fW z`L+Nw&vt<5K`Y~cGNnveu@U(#6iO2HB?#G?&R_soJCXXhNwZ`!Ku{OlA#0s+qD0Sq zIri#WfdWkWQNgzc)uA&CKMzoL8#=zu=He|vKn{V;lpsq$ikRKRY*kqpJB$b^S+XL^J~+oJj-U)N^4+gUl@ zAQB&2ftCj&UFFbCU|qbF49BBZ2eS3*- z33O-m@1$riQPEh4`bha-!`uSOWVAuRCN- zYBV$QcQCS<^?lK$3OcC6Vi+&nnN<`6Ee!GG#h$C)T?S527VMw-sH)_MI2&cdXpk$L zfBjukc+o33A_5DcO4W;gUWnc|tpn<*atZy`0f5p3Zoxx`1WDTRMSqcpLZLp##M+@u z5F5-2M>1tn<0i6cg-kCazCVRB^-U- zD5Uxvzz7;|O_QeiS2j13-l{6dO3Ddrsfxw_`7>)CjD)|kquYQ;H~>U|5hb`3JUq0(sC|x^Y3_1!M0eFk2dB~_sQjzeNOe39k-o^>pNHPv5 zcxZ7DCt=_qQ&IRN1Ci={lf@JUy^LbW0^`JfT%B64kCjj&EUx%%>|#u~ZINFB{oHbt z(rg_n6eOd!pW}zqDbsX(RvD3wlWutqfK52ObP?QlF7%&k?W$HvC#ps>iLEFocGX2W z3j;w`o1s_74(BJlV=uL{j?gjcPJZnsx=8N%=gG#8z>fqxzbxJ28JEG#bG#zo4$eEDv+U_oYFQqf~; zvQlo>_ys#Hqd0nfh>YxIdwV%_K0m z4g+}TSB6QcSHP#&aVrH(lYbgT7E@Y`h<%Fb`J&xG%Z(IcMtP;zxg)b)gC=)AZF)$C z*rRZ<#H*+~anO}0OHMFNYZM`kGV#~CtL-H}MSQGaTwUa-7IBGi9{x0#?vAPl@cbX^ zjRkDI0V!r9`UyM2F#_FNDK(!BLci{_GS1VT(cC2`z6BoGZvd1$PDj50d)6>o7ibp2 zRa;j3(>j1+sxKieUGO{XIc`SO@9^^!U(l1?ILyxYLp1_O&OLI)Da)OVI#voU%l!pJ zpVG3kt6W#vfexg{zo!)r%w>dCuOU8dubE)UFOjR3j+*$x(NwuXZ`WMfBo8%Llzk)d^76jRjN*IgQGp$aYy_mjF+ACgNvYu+B69 zrl%k%$_k$pA_;t_2=5iEB-+;dU ze`_iz&(X;PD8)2c4^Kg^#%3c^s-HDLB1>A>RnyxeV`4JlJ<|X^*93HIT~A-bpaKxO z?7=?t3R<48lKBAPwc=K=QpI0I2-`R4mO6((BRTQ{M|hrobCad4dO}3I)F@DoF&vZTn0K zgT%4D!$Y-JKALlzG$4EW=*yX#gXRs~QUL-%O$wx;$>4A}+0r`h{o9Fz4uC2$Bh{|) zSRHgC$3O>A`MI$0ja{u1GopFwy$xDzim<3C)d9(TtqKOjuo_TXB7-fZ%dREgur&Z2 zqtBFIX!~-391vyKyXeHX!0V&>J`to4(cKz}8fMX*f|9dX3(>4pr|!{WIQSYElOKcl zu;#IW{u;tU=E0CLc)n3kf~b07KeE{z)q0QpD zb!oBp^G+^4rTVjeG7emTL*qC21FfJ<>VOdRzXglJ^JV?V0C*16alIML#ZKzS`QH|| zwhHS^`9V=`3rkrAO&GJ9Km0J_M<G_?Eh2}sRr0La8< zw3%zEmv(6qhIf@@Q;`<$`M3~dP@D|^?aeR+{l%8^IE?O-B7U4Y05z{%sH`)M6J^PU ze)?`Z+fX};V(;YC3VK&C3u{^(S~@D)So2=u?c{_Wz#3wWp%QnXj-l2%6$4ol{3I`d zq<^@ZRe$(X0H&r2E4$YFkJcpxNH2|%ZrshIe?I#EeIsTvy-)f9T8^+mqfZI~JfGk_ zkrU`30Q1MQBaLt5uG8;9@WgL0e)!kr9R~*ogb04D@eS~U1Hk(Y*rfc|6~-Npw&t4k zE79zPQ>==ZpN$ihk4pZY7>kC{lg_OY&@A}yUZ<4DmI)$f#>F=9Gc~WvpziA0d-X@j z;c&Kyzr9b_e!OoFG|oEVdpcl8NJaHwd?4z<)9aDS zoNLjV&gT~zEO|5i6|;TthiIq1?B*%=Y=7~~;Z~QbgyTK$ucq-U1NA}Bs$N7e3L_vgl-IGM!K|ei@~SjtF>-H&oTd9AE@{aDOcu60DW2} z4Ig{6K6qbLR5X@JM@?^fC*oVnsuzqtX=yA~aw*69It5OJ(lw=eWUI{)*-&Xv(;oHz z25)gRHk!4|3A9o?|Y~+0rvr(4(r(lt+(s(S%V5P-EX@AsH+tn*%xi$vrJIFPlWR! zD~m2fT7vRgA%!d5L`XhefoVdm|Ngo5nLI~y_2!H^N0n`#op6^VY3|t-wjp;~PDkuY zI+NZCaH3H9CKachR|2KPJ<;XT`gf@XU%3i8Iv z?W`Wfwkubg`~RI@|Ge^=CA35>_tBqTJspR``t}+%DdEw^PNu7sH5Z=gfZ(SREE2N5 zU;6xyR>NEWu&ptTF6>4p8~+y$DGx=k3df2xaygM`$&PBi!lk*o`fYX{pHSGWuR`+e)}QTciPh}E0%04?Ox-IbcKW{3 zGI^hq{&pITQ{S`9Of3(0 zBYy4AdQkR#=j+wB_51hs_0JC;v}Va}Anvkk9o|xt5Zu}A|G>I42k?AmZegkZ zd4WR5u4Ex{N|fMVhRZ8sENuaqNHpBY=EZ_J`UF8_#iEam6ScnNin&v*)f%{w^dx=` zANNP=9MmWHJokpQ=hNeGt4ioUI>x`>V(;GLfxa`XVIaW>;D22s<**;9Z`Y{pmG<$I zxIk2NezVK(m2`A-V!y7HDN7Bm4A)_ad)M^;ZEJsj6RUU(fQNaIEAvZ#@OVeI=D{A> z`tNy?6gwr6MmeX~gwZ0iFz&>_^6yR(PmuZ&4`j7)0x6Ge|9b=e{3c@%+=@gZqoUr= z)VnT%N;tqo0iZRZTU*w^GF(!}b+^KGSMQo$x1YI{n>~nl{tV!Dxd0+qpXq$LZ~<+s z{|b0>BSk&b1Ts{tYxNyqg;D1No3IbFKX_kK5^^J!`8-oKHZhQ6j0Jg-!f{0Z zzpLawPva-hSi%vUjID--##hS`S%0e~Exiyx*y91@8D7s+APmy?1wq{MgE*8iGEP)T!xVE|rvn|_ zPCQz)-j;Mnz0TNSFECVtB?WRU4hlS8^wb{P{zFADkve{s1w7J-cpZ5#`z}Bb-`6z)_ ze0ds<&;8s9yll^{;#ZuM`xXxv^Xo9CoQ(jSV z?Ao0+{pAF_5&8CEd~epU5$(ac9)zP`mEKj2>md>e4u@Mv(Y_<*Q={QUi?>FFikj+r2;ek~-v6I-OHq-TE&IAQY- z0l*=o=Qe*IQD$BLj7^eRN)Drq*7*m)vmgHS>v#p2_BU!3Q(l1O#n1m;=IAhTu28x3}Q$Eyk%i6>r?6UHX1%}cSs;a4txGn(&@D~_z5#kE&_ELZY40Tdyvn!y14iu$+k{rS545-S1qm|DB#!EEGE}ISBRAWKPkc+_pg&H1WU z!h8tZfBeXI;satw2WaJtT!Jd==^NsinVJ1RCI~QS8op6sHz$k$u0VnS39uNa-tyL8 z#l?+f7wLFCm%-CK*VC9blhr$QH4uJGY zc8~^#ZwU`_sVP`^(Lf=?CxOHcj)eZVg1~AW6dKwF@T+WDSq%QCj1ev4iE+o7eD7J% zK6cG`7Z(?UAr<}`jD7Toc`5Mv8oE1F=~SEUh|LTdqlSQ>UIaf?FhomeI{HkdV2HSL zB7ngh$E zzmbvmD}+tT_6(cSYwvwsR3@9_haZ6Gg{l2iC5JJUcRzG7xKyCXpmnPX>oSagnpm6BFNpb*vt1wz#;6 zz-XPFz?_0ZO76nxWR18!7LCXMIzoejI@7;`cfI+X)uMb2YzgrC28f!PRAdflajpTD z4v;O--%>5ixZj>yITEHr?`|U85YLw@5r}J@P1HLD5QGMODV-QM+2~n)qc}j9MQ2Ez zuSct33A8E2e^m~6B1r1ZTLnOI{#?q+l56_(@BwJ>8f^^cTgFF6zV>ZxZ5=s7Na(S} zeL`SB@i~5A*ttF3zXnqroG9@q>|jAASxZYeoPw5-1Q-KtdgMB#A(4_TPnSg%CA!gM zfH?qvWdaNmwj>12e=jf^%L)HXMpl+deNUQc#tYzlmKR`OQTv{LfQtF|_#LQ33?9IG zre3H#qne(R(PihJZ%cc#dwZkAExio+*Az>G0i>tux_D-ha>M|^qpb-%&}xygw`aq} z#r;_Ln40jdxVZR;+hA&ikm>{V?iNIzfTgB8!m6>@5rM$qUOT)W8|5+;Ht{iOGPzk2 zO3+W(Mf4LNs=Ydx7}WcE^&1NnwKTpN$bht9S1n@vfO!kJ>;gj&{tjJQ?WqAi30SLK zu+E~1uUAePndixt$~9)y=y!k^ms(G72!bHZwUL6)9+1=S*k>WIfmR}sCr>(!$Ym1@ zo}yXbBpm3B`+GaBEOwm;0T1TJEoiI4ecHt*Pgz2H=Kzy-a2mC+73g0i{vq_)d*8@N z>}harTe@kvf;PHk8rMp7mMo_(8aL#834nF5hm5T~K-QN>%o^2b9XDz$R?tz2E6eT zA=(#6ED7*m1%qUhhk&(H>tRuNn!@KGX485w2q5ueY}HqnU>AnXR6$^fXijP8o~p=h z0m-O$k^;nn@M{HJZZh>;pC%5Oezgh@tf3cgfrbV8|p8IU%=k6g+R4CyE7PwU-tf60i0*=|?U`ghh zeCSH1W(B~89UvR(vxs+XCyVI)T8vyi!l{b#@=|bR81Wv{bE-i9jO|KQ?w~ z(KI^6>*j#(vC=1W+pGs1@E^dWdHB9*Lf_at(D2w?wlpiR#y;vCeLhEowjZ40Y+jd^ zE?1ZVxXbeLXR464GpQ)KmMegY)hi!-L7&-u$l3B&j&JB zT)OCw9FALE?9fy?Omk>n6gsO^ymYr7X~zx_(TX=b%h(Rw_4M@M+Htj|@4^uNFvK7L z?!v;{mL?R_G&ZK0cog@)H|;#jXi~V})lXo#Ri2@zRv_%D{eZCXJ7GIg6la0`H~VUC z!ZExzitR7pFCp^`{AWo#HDLsO8#X0n1jsbR22jh|WaTq(niY#-Wz&yvnIt+6 zR0Agk;M|)Mn*`|~d*EqhfNS@~ulq71=xFSKn#Dq-A=dq3U6lQ8dbT%T1H6$SO#*Ek zv?&NLG=DIT@YB)JDtyNzfAJWDkOeyQ**%WU@96U}V)(~upot$BA+76N5JD08_3nKr zBO_Oqk)ICcXYiI%8$QrN!M%y9D#*?%?oIm|HdCm@$sOAJ;i%da80aj$(3rX|$}1_B zSu(&|tOo0S@-!hQIV)=Yxn2ViqVacaS~uA51{ry*y|nbvj|=RdnQ=043UQKgu+iqo zYvq%XXg;5-8uTOxn?#OrA(*QSJ`)KJ zf!BDdlS|KYoBQ~hicX$xBp4mT$XiwCrI=mi4SJ>yaV@B_#Wo@=sMp zeuek=pHknAM|h_GTm-MWWFGQ&%>x=CAO=o?8^FdmPlnbAamLV6IvLG`5(DQ8%rG-L znkracJ7+9Ug_e(>KmW#YU+;n#ODm$PQ-*|CX+`wUnox;@AyI&xN@LRw2&wdNk^^ul zfTQzC*>`BVwmZuGmR98I3QmuHvi-(X2>gGnBAB$7&1JS(&Cn7+GMmO)ik&FBb@9Z0 z8xXlYSAw~BdcBRLie-P8|BAL1gzm}0_@doo`lF4t!}%?4Ae}e#mx28G_Ov!%(rCYQ z6K%P7xm>aD=ulrd*KH13ul6hRS3Suz*thUZc}CCs2Ye1FTG|{qxH3 zG6R=;+?Zpr=xntximAd+T7)qIuAO-2^I@c8c*p;2EgQZ?S2a9|KnzDKb02sGC&MJ= zDI8WPhg2x3QcHnj3$0?DCe66yV-PeqY9*U4-Z%u==nuB`T$F5a!Ofx4Vn|BYzS?y_ zFVV6Vl%3FOO%qa!ib))S(2Zwo+~syAb($8;X2r#Kc%= z*D`JjEgDK9*I&^mJ6|v>m18>gc9+XwcU`!dgTlzinV3IabGWPRqY=d{qUm>`Sk4e@ z6KX>@r@P$9@>ku=JBa)^cu=q1`A{A)t!1N1(l#K*iSj@|Em0+o)L<##xqBRG?^4cl zcYRxm@>M|k^)$0fw#O#DLnMX#Rob#xUibWPGW3(!ih3CYwR!Te_KBTNTkHnGK`lK! zVMWM=S3VtWUoB+hQ7x5zTo#j`&Tm2vZ+9ZPUIXaesF3X{7E8H3Da98M)bFS*pFVj7 z;53vAZ}gt@#jKq^J$1D#*9*=0Dd!J?kH!SSWJ_ z@x?9I4Lw#S=(eiE^+hBX!m5#b(5)P0ka((ElsO(6d!s#Vwk~muESu|eBub~IzT5Ei zE7!<#YBIFN@gXq4#CZ!XQ|P#y&b3PBo1)m89}ja8Wb}qa`qzEiP3rez+OEAaR5Ibl zi*8ZZgNdPL$nhrbhza5&x~YiVyT%@2EnY{)^ewyt@*2>{yH0tQRUYOyyj0(*&R4*f zMJ%MfIqcTyBlGvE#l}Ewd6*(5tzPSNhLMWFM~92FHJnM8T+5CKRlUb3j4vDyL)jZ> z9nmo{LnFfx7kLV3ZC95#wQ263_l_xgzdG+Y;t8B23pWJH8L9jM`sE3;0WDCKqChJ* zF)%7JNRX5cl?tYooRR{oB+Q%)iu6$}5(nms`=t3Sv@n|`-^PZj4zENhI%rP^WrLUn9CHL%MGJJqW(wV}iy1xv>%XW|y4jba-xGnEhU6w>b zkt!8s@#+b_e{PM2*JWpeMn<&67hUgba)Yp6u?Qz=;6AmU?Hzj`5fR~`sX@&Sp?bL@ zDm>+N*}2+N{@7Kh`^ePC#BI~`ISblT$ll8;PlQVF153L%upShE&&8-mP0qK9a$82z zx6B6HqPDD5EhNPB4sL++4Z7y>L{>{)qy2OJHvk>yCZ0rdbd{GNAmaP#CL(d$ras9; zJxm=%TvH}Zg)4OC^sQY!RNI9~lqD;n>s^;CmtlTi>G+Jcra1IZC}d=0EXjy9n3!d- z3;Et-e_$>l^Bhbb1vl9DK)%W^AHcm6wav1r*7ELvxyNRg2)q5O2}M={%S5ipZG}%~ zT_tw}t}wWSsmjO*>xUC-r!jUU6cQ3E{v7LEotWHFRn;ENF0#>j%zpwDlM#>D-A9Ku z=5}?s$e(T;uVjC>do^V>b2%`!zIsbIF@=SRTk`_c4X)2G+jrj!2n-I)aVr8WOFhxsSI3CRARs%uctM`be;;~=X&)c z;1$LutEw;8b^qtJ5dSRzYKIf9{+&Tch#Xj)TYy_ILl0PF3v#~OXn}h{s@H+c$n!oK zq5U>IxHhwP6(%SPK@QhdjQmg>Foq#g-r!R+GvrpIg$^~cJpcGs|JUE~CS|latNxd{a()gHSxy^!n>ds4NGGM13)uU$?yL^UTzwwkRyixt561m zMn-gTSy)&Q6$Ma?^HMj#wYXW$3vu^JW6gqx(HegHuRZ?H+RFPF^HL7DPwj86+#A3J z-(&70J~?@ja5MO5AymWJG4?;D#aQ(gw5>n+-FK|B@BK=i$f086Ep#SJ8@qqmz<)%1 z*j?Uf`+H*I;;=ui!rSZJxA6K5vKMbq5p>ZEH8nK?Pn!w%y$f37bw1>wt$sL$K@*<> zwhld-lQFY=$bwbAt3ZS^PyeM9{}i%tB!ul1VMXTW=SLt1vxMWdPK+Q`1D%qRlFR(p z9q^v;wzRYyBsnq3y0}!4Gy%rP(ktQTr>APApo#5`n%57!<76P(E{gKVf$k5!G-@!K zTY01xp2hGWtdW#U|0;njy%+1Q;9)fbEDp=SgJ&{C7yZ<7037~lK7yb?P7 zK_)1bIJiyTCO$k}lb!XBZ4P@7JiI~2WLvJiy-mV;(wV5EygW+(YggC(VqP#maJCWq z7vmk_=j6|X^mLvcqR@KT@aRx}Y}*JH_Rwca0Lcw0ABR&buqS6EcQsz{CuOoZe`bAc zF}1Mq*UA28jmE0%d^ah);Jcfo>LVdlvK_c)|=CI8D_!pcw z7FMk|wJg%R{~Xxh*?^F7I*ELzKAaq}+9Vc*cGXyil&VT@c_?QDI1rG&_q7Ce$BbO%##>-v-G-Apuh8RX34}+^` z2yj{#oSI%p)w~8Nc#jB++4InOKC|k#Z#h>U>xF+q9UFdsm_4K^Y=)tCEAtk}tnh~N zJcF1&WJGqW(~)W$=HahPy(qyUES#h}e>jPv(<~-N-e(Vkt;VR_FC+MpV`Jx_@f8K6 ziL*Ol!q*QLn)LL39cny!Zl*y-v)WEWFXuVGgw$Vrv*+1~b?WZ%ZDhpy?xACpHyb`F zZ<}%0i7}&`KBH}8E1O*+=w8d0kZXjycwvo&?~?J2xk&At>(m?tfBR_BRG6%a8@PbY z!9uX^UOaia#IfzEs_p6i>Q;4!l0S)--BAj+Jf#8WV(tlY=^LVdKH4}@tvwS2VZT1( zMN+Rm(iNQNb>l-qacfD@kbon&{^~U<0Gpw|CYgo~d9&55SQcJJR zsEws*jLa0i?V%tcg3>scyBql8jv`vw9Lt~TEV5IPv7?~{ox7qU)qA`GnKdz-I@;${ zdlOFzA(~-%QNm2nm`Z;1)(#2U6}OQ3NIEIFr~*k)-Tv3dxWq9(Ud^m(^RgGN1)Nd9Sxfo`I>bclK(3N zkSvx9Lqrj4%uVp|`w_|?R9=3pf?@VydE4my#qUv5nvn`tkz1$mJ7;miBGDW+u4rSM zK`6*Q5GnW&Y=!z%I~s-5d>rD+>pp!2%|poqeIok!Pyl5@q1bj-KckuD@qf+i6L&! z2rQ&y*xovnSMBKj7BI>?DJrnZcHW1uT@(Dzu_672fp`D|H!2pzEj;x5U~rTD^9-6W zt5qtA=V9;Cl0$^CL-H})Y4uEh19lcZ7WAjttj&2b_hv`|B=NykUfu<(?MnR8aMcsb z$jonD?13)jRYy|QW%ZDDX60;&JeOUVX-MU@g@s7(R{H6XgZ?wQK5YD=A~_rHVlwzC z6Ao+PC!2je5eY4u+-Ew!DTnb$XW6~O1>=Nhu9FN`HV-a*m@x`V0+>?@bOU7Jt5nF{ zxBDoIY|c_hgZ=T*?6oK{*xc;sPbVq8S-bF=)9(%O4hzlV<~eMOUY9~k3Ijc ziuFgmYS9q)o$nq)_7ttYI8;5A+q%p%#U3g+05bC5g0bAYnrGd%G^ICKx5U>L`?bY( z_W0Si<@et-6hz`G#yV(x(nfpwDDQc8d*(xC6Q7g+clTO)r)soH`&64rBaqh8Z+b~n z{@4pgSOAqn>SXdqQ0!sAn?BGMRutNuv8sREFwhKarW>_PPLb~vBm=EVktU4_d7@y}0oinA?-q=vJLZt(%rrYoHxl^0GVr#IpdYfev zCe_(`1IG=!t%1{5=4!=C#Q z+QRoKG^?@pr5*t;Z|;Ur&p5!`%r}KhYITxX_1Ak^vCD+z1gsly%BcvVXd6Q0doj#- zDUtglq=%Kal{~ZvIa{&K>d}psGj^~czHN^MrGIj8FFJASTo+%JPCWL1V(~iv>p^+c zg(w|^Ab|n=PPPz|Z!2~q&%=pjb#lgg4!aC_$MZs7s9vEqyGtLaTN^9Gt?3#_bKz5; zLafSrm#InXT@n)CA8c=ArdIISNx+%K`|luEK^x!Hc8(K;m9n?8dz~Rm_(WQs&*WWe zQs6%^oW-=3R~$;!J;+VCXjMGcQ60R}KElXkJG$#GkbrMcelRXv&VePLxvVkz|kcmvG9T) zZ`1!vna}-_ipSZeY`&Dy^)#HN3{fy^rhBvo6~NqaHFtYW2_}g}-$srN>Q4j10Agjl zd#CUtz>37>xTW0xl*VJ+W^E|r(Z*=`D@kt4FShpxr$>I`_Z{m=EUzM0QPkws{oD}> zkQjkUsVW)V-rqhKdlczvk(vNAHR|8Y?^~2(b?SBgHmvd;AF7Z?c_*nL?45qr>T{RwgVN` zl=H7LuIKhg2M3Eja6S#9;3P7%W2D}8d!%FQ@*$~!(~(0hitQ~@YGR@?i-Pj9wcdL- zG^tt?qhay5?EGm8l5CByA3HuCvjz@(a~pn$WfrV@1gqf$%-p+upX@H|94s zeInH-`*I_l*~B!PdVQPC?>Vo(6Q-^A&QK1riH#CF?8p9rDm4q4korQY+C_&U5dm4S zA31Snz_aOTo%!gbu&>}WCBpMvvw_iEK>pnieniEDXDzFsdeXoT0wTb==5pn`>X4*z z@n+%(*G_Hj?)xb=+VuG9tbVptbjey#syumjOZeC8XLo`^5J!4QdEfy6p$%{28u-@aeQS~$ zA(3xbvL3J)-iEijc@L-yUMw-L(M8B;v)RttHk@yxkqNm@aN(-d)&+6f`J|sT&w@!| z`n%c;KC-Q)4wF7YJ952euXZ)GW(F{C$aSFp1m#O}Il@cNFgn8LpKn^?=5pZh$V|0o zjvDKP_J-=QFVxMy%qDwWussQhO9w>(EiPCL~IyoG{0P)nJw~xcr*N#8cTPJAnmv;c<$MwhrXYW z_qUh)zoa+4l718&_k+4f{%xslrYQ?=ZKukm#67MWtyHCjB?tEt3AvCXcXZqZ(*&J) zUgxKAKhc#%f`&w7hiBQHDoCg49a+acpMP+0rS=D-7LylB%aiU!rGhK8r@aEMYh+N>+%@%Oz0r?%kc< zG!aTb2G;<|YaSSbZV_5F8_+`uCTgG^IIxk8EeA)vQo;}(TkPkmmPv6-x}l;%aAef<`x z&gGHlu!P#-%k^g(RApHcDPkr?x*y$StRNOD-m^uun7?LY_Moo?&NUn;ygVNGDBYilp~$eT+0ntFjc;@Ob> z$;)p?#5w4alRk|!vrwD=px3p;7pd>gDji*5wp%)(odZpKZ@2|G)u-@_i@dPUbTGv{ zH?GOrx)yT|3wAV|jf+0(i3;577>9^wBYMKjTII-Kjt4Qwzz?~SHZr1_bzdb%7{Mva zE$d(3O}b9YH8?a=^2GsTh+0Wr{h_2>PWcNvVr@7U`oJy`R#~#y!r(kAqT#zd-BWtG5Q$q zZuiiFZm94X56YQb?|*Um@S8O~mNOWlI#plZCDrYs7WC~h^|BB?cBlAMn&x#@73FF+ zt*zLTg{%oWr@`cbHq?=ZPgbkS67ok+vEBC8^Q)8UpWUB}qk0_OKrMRTQILWNNrY6P zw04@(@1?Vb7>8-_@tEKBlYR3h@M7L{_!_OPKT4hq6tUdg5RUk2_@Do^e= z8o?6$bBnt;G=g-NNH!t0nzY3qcJc>b)#-$)K%;}I|8Ml1>ywj$pApFg;!N@|& z_#4?{me`fq)Oh|GtfV=2Y=OBU%H8RRhb1oGvuq{3`7JlocT+S4?d~+`S_<1ThSrEv zW+_jeVZCYT9^37xK2H<6lO|t_Cq6kc99Z^xk+ZU+8soZjtLyazTU~mi{I0a?`{t8! zLjfBt{cTs%hU`=0!-k!BBUgT_jLH(i43leSz(-Vs?lU-7@ryo4M+~`!}L9`(0v(?iBB^7^|fy zZAfBhPP%(%Dv-l?dGSiNfyl`V#O62L!P}&>k|>KG+sVVjh~gi+*u#EPYP|; zi_gN+e^<2yeweR1W0oY1*xX-<(I>w8SCq}=dyk;r;Qzu@8OWJ`?J^ocx_!z1HudP( zS~$7S88&@AljvZb;N9Q#BL4hk=7K(^vjL0uH4fFSE>QV~^Bo{v(XTLTJ10pag1QDE zefM-wFrP}+XK1jTN!yOrvs0ESAN|ZufSX!t#zBBYxxoopdQnVzPNc%t z&?FC+7>}7M4(AW^pHGM?va~3*MT}llkBH-n&@_%)_&?6_VeOy1I4nDTn@Bk6!sVPz zaW*jQ5X+(7P9u8K8cxfWRgj;is~c$G=jD^Yeb>bXT)Jvob-63z!jHTUw%#kk)vX@6 zTpQGKp1nD)L^pFc{H0v5a$J?0s7UwqSVG%fI5As6YjO0k!g4K9x$8DZ*w6?{lgkaJ zv&C+9VxCw|RsSpG_y6`2iaFhDA#-Gbd4y7ir4GUxo@ZDT{-ypho#i$A%ozK43cFZu z>-X^9cX4iGefDHSV`o(?-FHWWJhOkK<@)3iwdFX(IVMMeC!kA{!ssHDy)4H`3L@qQ z*eX=5;>`7m35M(8!t>b*%?fj z0w&33;O(WE)me4khdS=B>nZhq6{{@wxZ#=Uo@${)bEIjZ&ecwLC-G@hcf)>&?$i{h zJmeG5_AC?!Xv)uig}e6*chEp;>OiT>uD zkY}k0F1xY)4a|!=s;;yGV?`ex_1XBTzv1?-|9!n$Y3e+d^EAy_^pZJuBX24XC&0X? z)-f+^dpuK!Ak^Z$aJi2qtY2TG_QH)ewTwH0ft+GJJc4GYE%FhIxu!Jfsqb=)&#bzJ z+Rk1}x7N(QeD#I7xz{NDkmJnq6h(;shOel)VR%4O%3iQv!iyfhi<6k;=gkY+qcblsDHN)U3fF1}gKauAMRnj-4UKCkM@=~vsVQdju8vjSV% zpD;IPy_!3l`s%Hi#Gas`#X9LT+2CYlQ~zf012sd=wsm4tu#XN=#MO=D<{m`JD{B`_ zrS*baSRKxM!~c-lK>AFe=Rsxv*SBZpm!AqObPJ5-!wI+DtduFeAGoyK_N_w=nwex< zi?{a;Z+y(;HKuk=^tVgM&C&<$bwvq}onz)?qnfry#80MZKAF6&{=F2Q_xZ>evZhq$!W)4Fgqkpq=o*|Uh>HOrRZ)M(W4rb zAf2ReP8M7H*x+TLgnU31IB^x=dxP z&DKLuZh+st^`N2DMJ#hWt4oJoU#h8jlKElW=dey16s?YsT6NB$C>N<0g7Xtn z$7NlO2~Qh7#U#MWS8}bpWZFMQESY^pT=CJNhfg~n-u*6xXYef2t1`dPs;qbU=e<-t z%iWG*=+Mp1vS->WGvc&jc5O3$$aG{L(i@h0EjY#Si7dmR2ZwmflWd!~BM$3RLr0AX z<+NIjixXv;a0V!uCsyLl@%}Ft*@OUv9SZaOlhrqBKmH*OX)-pU?cK_Ly+M)-;g}|? zwJi!i@#nUDr?>tzcjoby7{t8|YQ8t8<#X7Nt~@_^c5juQR%<3}{r#m^pB-0^X!n)M z9wlM$nT5orCql=YGl&BOr0IC3Hgw}P>}halfV(XMx}qGj^&8jtK{@7neF8;O;PfW(J;y)g>&UTbVQ3IlOY$vm?) z&TE&0Sdc3PmMRVD&G+Q&*)K;@diYY^F?)=w7X}zh3e#-We7>pM)OR}`5!<7@DljZD zC;CNhhr%VMyDgGXF=_VGZ5?CCth*+r%Ah^Knv56g6_>sAQN{CLuf*;(lmDD<@&)Xx`MBpxKJzF2Q_Cn{nbBtGBnm)rIV6lJtDmLSTX*(Bd%tVm#prK zvkqTafAq(pRLvLBkK%n%`=)9LHKnr8$=C8({PbFOhFjqp^Bao^$~H+nQ3hdE4@G3Z zXJ(EjRL#e;;#>^u@~sa(7V3ec`>~w%JDsucKQx^5*yd_KaxYH{zr6Zd7ql`xO>TS< zAMpIkYg#Y^j<92d-RM;ZN6ApM({PRXFHGaq21l<3e(kD?lkqPu*uRhL+$8ljBOE9b zJP7lp341Z@{UuPVzZ&1B3b08$)cR40`Es~B zJBVmLQ@`}H$gjrVyg&-Vz@UFT^mDv+e1YO)b{d1h@ke~kl*piLJnxYoD_aU+E8s^0 zeWJNwxv2QkKlQGW5vDJ+0y+Wl;+<{BN=dZe#S0Cp4rOA0HtdF$wy`MFX;ix|=Gv9~ zBj+6FtV)Y5L!FU&-rJ^b*D4K%6kR`I{?H;w>$P#6Su_qmJ;8e42R2)4)b@vm5*Ow9 zLDM=?B3aNHsidZfrsT$_8AsGflfp#Yqe?a(rW?ypb)SUIn5%xKIZGSJkrmpxi+a$U z$Ya{0Dd+n5M*kg#7lRb77~IV?m*!#}yA5uyfNeDU4M4LE-E&ifZ*{RBFoQ(P4%)98LMEk;#MkWy62 zvYRxyD7L^0`nEG!QT21BA4I8n?+0lp-rFp--*k;1CGc&$M}3z58^!{GK*I^TJLi{w(tFra0Xjf z5SjIh-KBvcN?}Lexjss9S-t3+joLF>&BTK-6T6RXrMa1rpYn_2{=c%$GAfQXi`Ica z2p%A~dvJFP?hxD|xHj(Y5Ht|nwQ+CUAwY2V0Kv6!hlbmknQ!Kn8iD}`a*u)J8OU!Oj|4Nplk94i|OnBMj9;F!?qD8y!{?=_}Z(Mw^i;L(5*tTt9Nc0Z1R_3BTNQZ8>fsqO2tG$9{a z&2-d&gVi-W!T>Qp&3?4;t4!lW8)0BxauXU)kq)5S4$RRtbmDNcxOKed>H3@YfYTn? zki}xYa92PfHyRkr`b$bOXrYrJJp+&bMqt5CwG(NvUoA6^sBcAZ$X?2-@66(u3qcP{ z^;}!5iLOO8d_5~O)M=%wZrE4A>gs%LWKG*eqRsZt1e!bZ;X*mg$y$5@&X5cm=0*Zp z+Tq`%@{yMG{KvGp=5HAm_Is0f6|h`ddypce-zO4Vh@H|fH*C8J7GIng0Y=16eRrN7 zH4pX2$GT51iQwbk&)k9lmak?5BBgx%GA&Z8T9zFd!5$jwx|SE6DhqNg-I-;2`%sUNhUtqxd}i4uc*Bod3QB5@3~u;%!U&Z_4Hm(8!C?$IWWJ?5`qB7upA_5;c?3m- z-jF=(>St|Ue8H0%l?(vn$}eXi#piQ;xcDA%=%df;L*Xg*y)7_y?BfY%GHYvr7g=8k zd~b8|XtGo2gQuR_C5tbv5f1x@Nc@Iibq&&x2A?;5g9ifrP3rTm3)sstSy1f}69d$gyF|d4Gg4rW*mg zDEA}!N^-7UshH2R*->GDBH^4Q@3Zm%Skc)(Q^HI2X1aSP)Dq0Aecx{EAeHO&g1Xg4EUmSJFt)OCvuEqOFBtOq=7A z6Dl zFr_qUO^`g^S>C)|k<^dfcUpU_lLO~!j~z3W4?LH>bcehX z-`qm)UY|(_DFRqPu6D`1GRx@e?u70P$|yQnaH1DSyztQBx|%*3-=$|y;xr0ju==s} z?PMkVeq(biHGVlK_$GlN@jHkDrEBq^qk5oQY`cR^sY9 zWr2UmCv(xZRh;CR@cHH+>Q$IGCFtIAKlDbrbZ>{;on+T`NA6a{XTW}NJ1VqApI5V9 zt3cEpmArMKt+go5z6f}P$%UAe2butWu6cX@*;nXDwDaFI@;I`jBAMzJ0@%Tk9xaNo zUA<|GUTPCY^|{u<0fDa;&}N|kYas$1^)b3RU<0e9nN5EBGJJBlU*;zBFp0s)V8)V| znAC&r*Drc%)OkWi;68$@KY^z^`_grK)%$a$@$E=(FgAHASU$Xx;1#c9G8x@@z?% zT%cq+uKbv*hH_++}9fw z9|koLSj|p!@Fxm2_Zrp6xmgYQXFiobuHQdl&}JatapQWrRcw0WLi9(OD>XA15J|3<#k!?zlOoGe)QIJ0hHuM7+rY+$b# zb~H~~(wGdQxX9hj^I3Vj5uH0*Eo0GjzBZ|m+7%Od6zI5(xsVX7+ zOV!wG%F&TBYV|UwK80YIQ7-A0?uowdGJ5J}WWgR4sdbyl%hKOv*CJwpi@R3KFHZ6( zP|yhe4f}zO7OBAkzURu90&{Wn^_b*63TK}p^Lh8iTy~Q%hP7_$*;OL@K*Y70RbKb)aQEE*2vHjldzY&P^IjLm^o_`M5X2;d|H-v##{Cq~mc|FVZS!Pu7 z)TXpX_cxu+dz?&OrZ1@4w-xg3J+EvK{Vzd;Ts7!9=+Y(w2rBPSZTKlu8hKz6wx8L2 zZL-Ro+un{aZG*KJW+~eUl7Ydoz5HfaYu#U6C>}5UAp8J6&tFoGy=LQVbjF2MHEoTTnVGV` zeOA<+3Pd?1CT@GFHXa8mh@3YW-25VU4VxRHKq+^S>dO#te^6PfD!IP^@n#R z{RJ@h?$*ee{b)9Hv5!{Ads7ac4ZNZW_SG$&DXvym_sQTX#P8q4%XMYl<~oO$y5mgZ z%kxx;iAh>;M+ZTlC;N2qKP4C5+vQs4WOz&gYua2N$m587J2yS%p_&h;eWt{b8TX1@ z_BtcCWoH!HIbp2@{i%FCWI?Ps#OYZdlid!OrH*_5u@ij>?QJ`>u7)39mILy$UOY~| zY^eL~^;!FB$v7YGIN&NgRW&kK1K${6Y*z$M*8st*=X)kZ^27vrt^C)GZuQ0AV?jd` zz6QxNXJ@zFmY@fa$1lFT6XJ_VU;a1#{{HZ!f7^72?|%X&VQqZKt&~0nUvChZ4>T0V zp2rL}2EF7OTs57K)bN^2prR5SXWwQ`NEhP+7&@yb{}-<@%t~ApQruqOQ9??VoIBBeGM# z3Qe%|bVrl?*&v+aA)gdzo9oEvJAqCzW8sh~r`JFzPL z3IK`Jq{&*9atis|l_hotWxb*bojs&ID2~8*rX$Vfl<8gB%FEV_ia;00E#<-qHgP`S zc&OHC!4YM#%W}?ivl;kCWskrr;L=wA&)TxulQ;`;9Y?`D;>Sf8tQxqp5!sM;arG(x zHUmJ#JIeO%{HokEWcJ1%3kCCnYB9%ZE+MDF)< z&&PWv41l`hQZC!>jkeKEmsuVk5EzaG_06}BV)JwuzPIoa!GN#x#&5MlQHjG20NA>V zB`9VxZy5-bTUM>l3tE0>q|?)z6LZD;U>N8nGk>ML^=-nEheG6vXjVY=SIM>{_10fV zt2A6_i@ygbcE3Bqc?8?%It88H9~Ll&kPfH_Q8~L5GH~eEgj;iD36+e9Ba1}Mr#18R4;<}R@F)kb`@$vtwTX7(7Q*0Dm<@y17F>Vs3a)w> z@c_c{DDT_|0xHiN_6vOv}=)e}Fu?O#~oL!*jLyKryVw%iDhMFV1{ zw8T0e2c<+}m(PlgE9^*12oC-nhaD{6*9`P zP4cIcL$yWP0fKX%71g@l;Z7#Y2M@+&v>XR9-#cD(YICn67d~-|PvTUsT$B)tD6P1& zlygx6HcHP?q^gd9rl3;D0?D=>`@Ts6b`oa5Z2acqKxv;^Gru~H!V@OH-ev%+;lN9d zR+uWM`*Vxk$9LTyLWt2IyS9|5WMIjUu71$eo4A(;?!S~N;UEV2&yD=-qn#<%;tG5in9M`4K*ZN(`6$GpyJS8^7pyeu}jd>tc3|xhwt-S zEg#FM&Z&y{EQSVq8hS##CZNqE^ZA+Syj^J9yJOhq%xHdGXi%OTRt3gb>2Cr8p1!^1 z4gznDEWJl^*B7M994r)4zMAyZpwQI*4<1w3UE<&Kk}`~VI6FrI6=yRQX+37q`DT#v z%B!Jy!8RsSOF5p^;;0&ITMaj1zCX&pk8{W9wV;j$%seH}HJ3Q4hryO_->%kFo5`LI zRJ5P_((E5Guk9GNK%3Z6p8BllP;LY8uIbN9s=Mh!+$=BMaXyqEJb$NDnKh5B)|O(f zCacKCQHd1}`VRI++wjYbTf{B?x|5_lbvW#H!2R-tTti!~!Je6Nap6)=h&jzN!~YRK z|3{a^!H9ydhnS|68>g#7xaEr%3%7zjOoO1pA@~8`sKO%jC{LWHa7+T)ZBJCCNv#X0 zfzV4d8T5U7u29>pG43(Y{;>3M+!n@?Bi?vix*dbx1Nl+73^2rYd@+(EN7TjZho+f- zl-R015~!pC+P=-;R9C@xY7ZG18GDl@_(z269pX1x89DlU8P5Q^f}ER>{drDv_5Awp ztZpAE7K2<%eHvhuNK)b5+!#Oz| zxnng`m8HxG(1B_4D>YH|1>{h_-GLK|%c|#Q}mJK-wZeT!du^(ZAFU ztP2aCKcQ|-hJ+~HJ=C8)SBk6t8BkV;#Q zLa);^_^~>DrVuTQC2R1~OYhuUr8ixT_T0d_pG%I$y#lBj7*1^8A6a2#Q=U)}yI0A` zDbC^dI26EuM?~-a+y|+*7V?f~*}CXcqB`m)RI;$xx;bDv5ZIFs^>;;ypmJ5M5Dtg) zpkI4zDUK`i3Td#w7J=36QI5LRPXuR#Iz``_J~Vfm#vi^WQ6&4WWep%SrEDBs{8?RC zB}dQ`*@$)eu~>K}-(GiQsV18ePTX;R`NMn~ZqT}N*%Aqv%hs);EWLE1xuBvl(}nhY zS(+NPG^xwFA<;Hw-#firKTBaPR@(59gaebk&h6|FGd#FDVM{%kE+!gGGdcFa!#-cs zTjl~E$I)(My?RdH`n3pH5A_7&sr}mfELgBETZi&-x0sB)7ewOs^q9Z2fiu2l+-6LUg&(GV z`bam+37Fg~w)SB{My$Iyim2L;{MwKRD&E71!R0ohA$e z^0#lp<)c>#pYPXBkhG2?+C{cKg!mlj z8ax5sj9h;0T0Kp{tjxtyYoq8(SSi?~ZTaL24SRG)J_kZcQB(&|6oSb;v~}|BPrmmb z>lzYWr#IFcR25gHh6a970jEnjDTW81c#h4fP35ia1F7f{l0SVl#i>>yk1+|g9r-Ew z?6HbiI+%UL*@q>13Urh~^u4PndnjlliJg*mTb}NK$*9Qs#YZ&KZL0P30_k^EX+OU> z-k#92(@5fr5|?UFQ>?yq|4n44cux{PAz+6zIA)AI^#kIN0!VcpkMfy)7RnKw!<1BT7vH7<_&ZgXM{B;|#3RGyqZgQ=c=HP%m@ z4lcF{Fwt8}ufq;N8zFBF6~8ua)u-RXe$usakGP|VMDu##ZJovX_YEcRx<&@ZG#o20 z6)$Jqp?(7FfKVHo0=JO*N^f;3|4u9ZuH!}NNwJZ7kiE|0ZCy9Z&|IY!^x3IsI5LfJ zkAYSg&(Z-*QdOCL``3ON0y(F2W}P=R@vAozm+FR3?Udi0)23Naq<^tPCe=zBmRf}o z@srynWWSjaDa>|la;d+Bf7OJ6oq`decI%O%zxwkd(+*58-U^tS zGh!C)(%q4v;C~5*i&mrc^hk;<1 zIr{139ZgH`h7l~O6o(4_Qj6>smm#zY3BNd{V(l>^4RvEGMFu?E%uf6PhKQjddK)ib7gU%X$_>tw2l9P_kKS6-tTiMx}18 zb2|>-Pz*|Xr^sfJ*2yxIfydU8ZrvvBKXfTnUQ%zN+(h>#FZNX;5R42SC$)|jbq~fo zv)vwb`x8#A`*nsG=&!X~GRs?TA(Ls_Nftqkdk?>aNa1E54Z~#C+!iC`zBEdwZ1^S40IF^vG4?HV{@Tx9_O0tEl5nl7@ALwjZ6Bv&wacUjV1VG+%(su zCUSiBXJ#9tvcg59OM-UBjjBeqO`pSeh+nyJ z02p1yEoSeJEm$C5rUY8cX4t#A9!xK|2P!-%r7hQ}TqJIr7ZBav$uV2Hd<-6vpfHwB zc_eX5NM!cXk$K|9EDV>Vc*p>4Xv8_YD2fsy^T`;t4jRv#74Ob)piGKTy~dkST3dW7 zXkLDhw9sOnNexR2)`qSTa&Ta4>@&C{MI${~U4T-X**6Q@fHTLH~_Fs;2y7 z4?2OJxF{xDUQqn;u396bAIrXdnXlGu&+B(Q$9Z=Dv9R-nGZ^gcgM!Zni;|gC0Y)4FbJJ zXR+cU#t&%LD<#lrL^ok>sP`806^&5__u#>BCtH~TDtJKB?(qac4IZl>nESmnL#{jy z4W<(V5?EI7?HjW9JJ+FAnCg{BHr012O8&0z^@=lOV;}J_R*<2(D?R^Vo2#{w*KFP_Uj)QLl*|RSbJa9`-kJN%omMjr@J>T|Uz^5| z!}j#k#V>B)^SjOLMIc|!N+NRRopdEmwn$lfOdAr@p#_;C)4 zVTf?vhk0wfXhXerXt@&}M*=iNb_FQ4E``u?n#4Tt5&$x#BfO2Lh*MJa3EDQoQ`^%s zO8B3S>FhKNGDtkO`UlWW_O}d+-*Z`CwLF1z*DaXVrsZe?cG8;A%Xp#zue9m+O6KJH ztY*$E3TGDFXDifeh{viit1(u(;{+cpry$C^Uyo|<#*qT1YVrz_k) z*H9FKxBbQCGtPx#Ji53bn;Om8xa+xgjT_!R#>+7a2Z5taccS1Yw6w_YIn*1r`j2R$d2rpe{l{<%1XEj8hvj}+J_ZoAZa@pD^UW~ff^If&EPk%PMe~uug%`PF z?cufjQGug`19uorvOd@|mkryg-oce9j4Trn^p7Rh0A29U@`$Sv3pF=^?A~%5)IqE8 z?X#RA#{RQD)GEWHlr1Z|^+b1M!MTys)ilY{j3`l?6)(xZl|vF%8jGK@PdiycX*|d) zAv-q_1vmf$@ zN3cAUXSJ;-K%(|=@aLFBXgs>~$)4+uJ|B5l*4yF8Y<@J1Bj1i}mw?t4Z!cq3*qk`A z=UZ2^%xs>qaV6;)Tk^E$phI_&vxsMT&xY;Bgp~Rb8w)b+N^jk-KC;9++`Wc^=VyrP5y%_2HCW|lVU+=(9he66Z-6%^dR2Q%L|SyLR)H7}?f<@tf~I*JnLHQyBZ;-$)N z_G-EDbqT@ZiXVjQj#gQtxbMLzqD@L^W=NIf$c&C_JNMbZbXqyL=NP%!xOJe(bzb<) z9(;A}r7~LLm(ygQHE&4_^RBJ1ksCL2{RP(8DDxf1^a7X@u(oa-u$J??4$H5wMm_Kl zs+D4V@2C-kuscINyKjAUSgAZ{l0{-rYh0Q0GPl?{&?ex9!eIy@vAy1F`D-Zy>iPrt zH~izfqSeL9;dH-VnAAKUu}5$NdJE4E0m3@NuZ)DNo%s&5j6U`F8Tb`aOeN}R2{-hk zErhvTEouPEV>c??Ld~wlzCHftyL8mQ8a6g9)p{ENyj4Rz=}``Xuc6JcUgH}cmGOie zVQ8}-IriOxx(!vS?}DwLNY_A0tI~=PB+E{89dPp{=q6I;hnxOxY;iN; zRQvohNHe%r2zbTf>rUrWlNxZR=8mO(p3V; z^oaYD!q(LMw5MyHWA1?!TgAgxsH|5?|k>oJB+f$o6^jJ3RX`;-=M6b1n|_lg5G)q zEKSopWBqHX;Ms1kOmjy6N$IPLqH2z7gB_CwFPf4JklXqS+Xs}$1Qt8BE~9cySd#1Q zYU*!7P8cPG^tG=dxOa-lEymz}TEYrXQj=Z!w~JKBGg%t&!Wzn}z~sQ6CDjJ2crm(A z|6mVb?OV)%tZBn`P12GTP>shfCl!v1mvW47xN~E(B5ExU6n08*&*P`jla^fF6V>)s zU#{Szf|~5)o0ZxR zBl9th)reHytd$xZnUPKJ~e`NIio{DN6$4Jv_1oWdZtgrPZ-|Z+Sh?HULZC zhA3z9q9T?G2RFiby2l5n8&Vrt>}K!XY++h0CynE8A2AgwchrnoB zrLXf84~&P_@lE!Y{@r7*IhR>DT# zsiw_uc6N_;JTyPkW3P_SWM-OM>rUs}1d>Cl2;EK7B$wXE{LW`;SGa?Yd{WeB9AoeC zWkn80ePuIqmF*&Iqnz2jXi2iJ-Y%SQ7%71t=B%j(9r{hD!y1!yoN0e_4=qVXD6We7 zR5iW523J=VLUz@A-tmT8ue`L=s0lQIv?dxslWrsZh@Er|1?ji>waW3vuXO3dM~@x0 zPfuO_X}2>t9fm(?mae9`1u^RgE6hY%$)3Jd+VWX2Z=3D0F&bS{KjOS!{J2Z8(2*Tl zcXs&Hv>-j6dK+;ttN`ZCsvx!;29l4eBi`MW({s#TM>JUO$HD*DRuVoxq#!b|+G6}W@G zHJaB^0Toy=4@7AN|C#-bJ+a?-)>>)DyKw^FU8kZALHiFI8Tu|L-H$$}R(XH8U2M;t zbfvDdPuQn75il6u+uS9rZydAN#>L##6~;4vy7Mhir%s!V0q!MR=+9AB$O$8uBYwZH zv6TmwhPmhHbp>(p{ndjTjB`Vylk!`pr%*DR2Us{R9{b9s@%?4$NW3Fd9)M1)6CpQi zHr3_9A%X)R-!8Pv(6rJ*A1r_Og%R=9G@Txp0u)-xEf=v z|9h*%s>}FvjIapy;NW zw7X{C%Icy`f;K4HVu& zl_KfOw$#b<<$%J#vD4xV7yd^#Bof~zk4_QnscwNT;)VAUsr46oX;q$DXNt*cI?$xZ zoWqDn#lZc8;z)c=)`(ore)NP)?$EkT&16u< zp~X3Me%$4!aG`evD+0^l$=~7&MWDk&#d=tdlaUCB7TQ#19dKF>D6YXB+SVKurC~#24@rxC6h=x`>G7yNGYNP<@#XD;HyX*FlN4a11~BoA*#*? zjgX7Mm~d)kfxS@k8$Z4N7t$Tt11cE#Wa$S7yGMh&9`iAQ3F>&w{1CkK_VL)a`aH7> zWwVpGK??OP_hJMz)o&Zhe3!m~a?Q&~>~CH*C$VV}DscQJ9Z^+U%b@wjnWDR}6XvE2 z{am+e(^i-?O|Fz2ZwF$miq9`!jB(ak8IJh|DMhRA(8LTy2JZ3tgK z^mC~vLstlnx+s_;v7$TopYK6VYQ-8%EiHXjvFNZ;;ikP&5|wGBBaW!peaO8~g13b? zFRZ*u&>;BbA~LNpc$45i4K0d@@8S38i5jVQflqkV`H4=K5>vt0?*ot0LwB~vi!yj!x-z^&O5bSKmYM0A>bRO_-9NIv zv{PgG#2t2Wjh{IVO&J^F6|CfR#6WWG2WmZMMFWH`Nds7NssoTJxQPZW1(ddn)=5#1 z3gc^NX@sDIATtf84R<_qm0qcxlPJ+8PB(p{VayVdtIq%=#^Xu_FA&Pb@qKAs9Nw zJ;20^zL7iKyv7%#ROan}E$&s`P5@09Ge(!Xt!ZgGJWk|FcldWmSI{{G7Uzs$TKAbZ zDYCvaE4g*X@6DZg`l%qLycTr><*F)Sxua~@E$M`pQlRXh6!%u(;y1U&QE9?OQ=QYE=k5M~Omev_tU(|1`@Ror9zmkBAUNC-29;z;K& z$>hJ=PM_Muh22gMJW2Ljj0+g(E8P{s>vPNa7vnR})JmYyD+P^zC|@EHRv~`9W2&LY z#8BDQ0RB5^=)Y%?3&OT5=U(ojRm%D@Z~Jl)ACT7u!lH)8U6*1 z>XI|DLInmIHB^_t^V1LTYVzy9p1*&tQH>n%e1UL#x>nIfEkemhP-tHIOjEFM@FXevtuE)DFDO6Uqwu-*&LF)C9=%KK<84O? fglq8an-|hyEb>;NwrPns&`nlSNupZpYw-U8l7HHH literal 0 HcmV?d00001 diff --git a/book/pulse/pulse_getting_started.rst b/book/pulse/pulse_getting_started.rst index d93ffd7ff49..7d1cdbded3c 100644 --- a/book/pulse/pulse_getting_started.rst +++ b/book/pulse/pulse_getting_started.rst @@ -27,7 +27,7 @@ repository `_ and click on the the Dev Container definition in the `.devcontainer` directory to set up container where F* and Pulse can run in a reproducible manner. -![Creating a Codespace](img/create.png) +.. image:: img/create.png .. note: @@ -38,16 +38,16 @@ container where F* and Pulse can run in a reproducible manner. You should be greeted, after a minute or two, by a VS Code instance running in your browser displaying this same README. -![Loading screen](img/starting.png) +.. image:: img/starting.png -![Opened Codespace](img/vscode.png) +.. image:: img/vscode.png All the usual F* navigation commands should work on Pulse files. If you prefer a local UI instead of a browser tab, you can "open" the Codespace from your local VS Code installation like so: -![Local open](img/local-open.png) +.. image:: img/local-open.png F* and Pulse are still running on Github's servers, so the usage is still computed, but you may find the UI more comfortable. @@ -70,3 +70,5 @@ This new window should now work as usual. Using a Pulse release ^^^^^^^^^^^^^^^^^^^^^ + +(TODO) From b2ed0eca305962564dfef81123f2da8d3b4e8d2a Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 14 Jan 2024 11:01:09 -0800 Subject: [PATCH 38/65] a bit about the release --- book/pulse/pulse_getting_started.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/book/pulse/pulse_getting_started.rst b/book/pulse/pulse_getting_started.rst index 7d1cdbded3c..0da02224a6a 100644 --- a/book/pulse/pulse_getting_started.rst +++ b/book/pulse/pulse_getting_started.rst @@ -71,4 +71,11 @@ This new window should now work as usual. Using a Pulse release ^^^^^^^^^^^^^^^^^^^^^ -(TODO) +A release of Pulse, including related F* tools, `is available here +`_. Uncompress +the archive and add follow the instructions in the README.md, notably +setting the recommended environment variables. + +We also recommend installing VS Code and the fstar-vscode-assistant, +from the VS Code marketplace. This should pick up the F* and Pulse +installation from your path. From 05d4620b46f2b0a9710ca4b1c934a90268fa7b70 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Mon, 15 Jan 2024 01:00:39 -0800 Subject: [PATCH 39/65] fix link --- book/pulse/pulse_getting_started.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/book/pulse/pulse_getting_started.rst b/book/pulse/pulse_getting_started.rst index 0da02224a6a..aa7fd7bc6a7 100644 --- a/book/pulse/pulse_getting_started.rst +++ b/book/pulse/pulse_getting_started.rst @@ -18,11 +18,22 @@ the binaries directly in your system. (Building from source is not well-documented yet.) +.. note:: + + Unlike the pure F* parts of this tutorial, Pulse code does not yet + work in the online playground. Use one of the methods described + below to try the examples in this part of the book. + + You can find all the source files associated with each chapter `in + this folder + `_, + in files named ``PulseTutorial.*.fst``. + Creating a Github Codespace ^^^^^^^^^^^^^^^^^^^^^^^^^^^ To do so, go to the `Steel -repository `_ and click on the +repository `_ and click on the '<>Code' button, then select 'Create codespace on main'. This will use the Dev Container definition in the `.devcontainer` directory to set up container where F* and Pulse can run in a reproducible manner. From b8e0909b3e3f2056b2a304264f3d7602932451de Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 30 May 2024 10:01:13 -0700 Subject: [PATCH 40/65] some updates to latest Pulse --- book/Makefile | 3 ++- book/pulse/pulse_ch2.rst | 17 ++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/book/Makefile b/book/Makefile index d6afde4e849..69d31d64d6d 100644 --- a/book/Makefile +++ b/book/Makefile @@ -2,6 +2,7 @@ # export FSTAR_HOME?=$(realpath ../..) +export PULSE_HOME?=$(FSTAR_HOME)/../pulse FSTARLANG_ORG_ROOT?=www @@ -15,7 +16,7 @@ BUILDDIR = _build prep: mkdir -p code/pulse cp -R $(FSTAR_HOME)/doc/book/code/* code - cp -R $(STEEL_HOME)/share/steel/examples/pulse/by-example/* code/pulse + cp -R $(PULSE_HOME)/share/pulse/examples/by-example/* code/pulse html: Makefile diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index a90fa63079e..504ae997eac 100644 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -255,18 +255,17 @@ We have so far been writing ``pts_to r v`` instead of ``pts_to #a r #p v``. Usually, one does not need to write the first argument ``#a`` since it is computed by type inference; the ``#p:perm`` argument is more interesting---when omitted, it defaults to the value -``full_perm``. The type ``perm`` (defined in -``Steel.FractionalPermission``) is a real number strictly greater than -``0`` and less than or equal to ``1``, where ``1`` is written -``full_perm``. +``1.0R``. The type ``perm`` (defined in +``PulseCore.FractionalPermission``) is a real number strictly greater than +``0.0R`` and less than or equal to ``1.0R``. -The ``pts_to r #full_perm v`` represents exclusive, read/write +The ``pts_to r #1.0R v`` represents exclusive, read/write permission on a reference. Revisiting the ``assign`` function from previously, we can write down the permissions explicitly. .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //assign_full_perm$ + :start-after: ```pulse //assign_1.0R$ :end-before: ``` In contrast, when reading a reference, any permission ``p`` will do, @@ -288,7 +287,7 @@ it, Pulse rejects the program, as shown below. .. code-block:: fstar - Cannot prove: - pts_to #a r #full_perm (reveal #a _) + pts_to #a r #1.0R (reveal #a _) - In the context: pts_to #a r #p (reveal #a w) @@ -345,11 +344,11 @@ are true after each command. the current value of ``i``. * At the point where the scope of a ``let mut x`` ends, the Pulse - checker requires that the context contains ``pts_to x #full_perm + checker requires that the context contains ``pts_to x #1.0R _v`` for some value ``_v``. This ensures that the code cannot squirrel away a permission to the soon-to-be out-of-scope reference in some other permission. Once the scope ends, and the - memory it points to is reclaimed, and the ``pts_to x #full_perm + memory it points to is reclaimed, and the ``pts_to x #1.0R _v`` is consumed. From 5b74fd699df8374e3a36207666a1b0a3a9afa8f9 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 30 May 2024 10:02:27 -0700 Subject: [PATCH 41/65] s/full_perm/1.0R/g --- book/pulse/pulse_arrays.rst | 4 ++-- book/pulse/pulse_conditionals.rst | 8 ++++---- book/pulse/pulse_extraction.rst | 4 ++-- book/pulse/pulse_ghost.rst | 2 +- book/pulse/pulse_parallel_increment.rst | 10 +++++----- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/book/pulse/pulse_arrays.rst b/book/pulse/pulse_arrays.rst index 70396137f99..d49b6a3a638 100644 --- a/book/pulse/pulse_arrays.rst +++ b/book/pulse/pulse_arrays.rst @@ -58,8 +58,8 @@ asserts that in the state when the function returns, the contents of the array are same as the sequence ``s`` updated at the index ``i``. While any permission suffices for reading, writing requires -``full_perm``. For example, implementing ``write_i`` without -``full_perm`` is rejected, as shown below. +``1.0R``. For example, implementing ``write_i`` without +``1.0R`` is rejected, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst :language: pulse diff --git a/book/pulse/pulse_conditionals.rst b/book/pulse/pulse_conditionals.rst index ff8e638c9e2..5b25d658089 100644 --- a/book/pulse/pulse_conditionals.rst +++ b/book/pulse/pulse_conditionals.rst @@ -77,9 +77,9 @@ We can represent a nullable ref as just an ``option (ref a)`` coupled with a representation predicate, ``pts_to_or_null``. A few points to note: - * The notation ``(#[default_arg (\`full_perm)] p:perm)`` is F* + * The notation ``(#[default_arg (\`1.0R)] p:perm)`` is F* syntax for an implicit argument which when omitted defaults to - ``full_perm``---this is exactly how predicates like + ``1.0R``---this is exactly how predicates like ``Pulse.Lib.Reference.pts_to`` are defined. * The definition is by cases: if the reference ``x`` is ``None``, @@ -94,7 +94,7 @@ Note, one might consider defining it this way: let pts_to_or_null #a (x:nullable_ref a) - (#[default_arg (`full_perm)] p:perm) + (#[default_arg (`1.0R)] p:perm) (v:option a) : vprop = match x with @@ -112,7 +112,7 @@ Another style might be as follows: let pts_to_or_null #a (x:nullable_ref a) - (#[default_arg (`full_perm)] p:perm) + (#[default_arg (`1.0R)] p:perm) (v:option a) : vprop = match x, v with diff --git a/book/pulse/pulse_extraction.rst b/book/pulse/pulse_extraction.rst index 45a4feee0a0..e31088167d5 100644 --- a/book/pulse/pulse_extraction.rst +++ b/book/pulse/pulse_extraction.rst @@ -337,8 +337,8 @@ and the extracted ``majority`` function looks like: .. .. are same as the sequence ``s`` updated at the index ``i``. .. .. While any permission suffices for reading, writing requires -.. .. ``full_perm``. For example, implementing ``write_i`` without -.. .. ``full_perm`` is rejected, as shown below. +.. .. ``1.0R``. For example, implementing ``write_i`` without +.. .. ``1.0R`` is rejected, as shown below. .. .. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst .. .. :language: pulse diff --git a/book/pulse/pulse_ghost.rst b/book/pulse/pulse_ghost.rst index 27b51f11003..19b56ca55f5 100755 --- a/book/pulse/pulse_ghost.rst +++ b/book/pulse/pulse_ghost.rst @@ -283,7 +283,7 @@ similar to ``Pulse.Lib.Reference``, in that it provides: * ``GR.pts_to (#a:Type0) (r:GR.ref a) (#p:perm) (v:a) : vprop`` is the main predicate provided by the library. Similar to the regular - ``pts_to``, the permission index defaults to ``full_perm``. + ``pts_to``, the permission index defaults to ``1.0R``. * Unlike ``ref a`` (and more like ``box a``), ghost references ``GR.ref a`` are not lexically scoped: they are allocated using diff --git a/book/pulse/pulse_parallel_increment.rst b/book/pulse/pulse_parallel_increment.rst index 087e4aeb6b3..f192f7f681e 100755 --- a/book/pulse/pulse_parallel_increment.rst +++ b/book/pulse/pulse_parallel_increment.rst @@ -345,24 +345,24 @@ of the API: fn create (v:vprop) requires v returns t:token - ensures cancellable t v ** active full_perm t + ensures cancellable t v ** active 1.0R t ghost fn take (#p #t:_) (v:vprop) requires cancellable t v ** active p t - ensures v ** active p t ** active full_perm t + ensures v ** active p t ** active 1.0R t ghost fn restore (#t:_) (v:vprop) - requires v ** active full_perm t + requires v ** active 1.0R t ensures cancellable t v fn cancel_inv (#t #v:_) (i:inv (cancellable t v)) - requires active full_perm t + requires active 1.0R t ensures v A ``cancellable t v`` is created from a proof of ``v``, also providing -a fractional-permission indexed predicate ``active full_perm t``, +a fractional-permission indexed predicate ``active 1.0R t``, which can be shared and gathered as usual. So long as one can prove that the token ``t`` is active, one can call ``take`` to trade a ``cancellable t v`` for ``v``, and vice-versa. Finally, with full From dc24c8fc70cc8e80cdf02f8c7ce97cf51f221889 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 30 May 2024 22:16:49 -0700 Subject: [PATCH 42/65] revising atomic and invariants, boxable predicates --- book/intro.rst | 5 ++ book/pulse/pulse.rst | 32 ++++----- book/pulse/pulse_atomics_and_invariants.rst | 74 ++++++++++++++------- book/pulse/pulse_ch1.rst | 6 +- book/pulse/pulse_getting_started.rst | 6 +- book/pulse/pulse_higher_order.rst | 4 +- 6 files changed, 79 insertions(+), 48 deletions(-) diff --git a/book/intro.rst b/book/intro.rst index 087524a7d3a..8a3d0a50dfd 100644 --- a/book/intro.rst +++ b/book/intro.rst @@ -81,6 +81,11 @@ automation, and compilation backends. Some examples include: a core program logic, and proof automation built using a combination of Meta-F* tactics, higher-order unification, and SMT. +* Pulse, a successor of Steel, a DSL with custom syntax and + typechecking algorithm, providing proofs in a small but highly + expressive core logic for mutable state and concurrency called + PulseCore, formalized entirely in terms of pure and ghost functions + in F*. .. _Intro_Vec: diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 4c49f4ad974..623851623a5 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -46,26 +46,26 @@ combinator. Concurrent separation logic is an active research area and there are many such logics out there, all with different tradeoffs. Pulse's -logic is based on a logic called `SteelCore -`_ and a prior DSL built -on top of SteelCore called `Steel -`_. SteelCore itself builds -on ideas from `Iris `_ and `Hoare Type -Theory `_. But, you should not -need to know much about any of this research--- we'll start from the -basics and explain what you need to know about concurrent separation -logic to start programming and proving in Pulse. Additionally, Pulse -is an extension of F*, so all you've learned about F*, lemmas, -dependent types, refinement types, etc. will be of use again. +logic is based on a logic called PulseCore, formalized entirely within +F*---you can find the formalization `here +`_. Proofs +of programs in Pulse's surface language correspond to proofs of +correctness in the PulseCore program logic. But, you should not need +to know much about how the logic is formalized to use Pulse +effectively. We'll start from the basics and explain what you need to +know about concurrent separation logic to start programming and +proving in Pulse. Additionally, Pulse is an extension of F*, so all +you've learned about F*, lemmas, dependent types, refinement types, +etc. will be of use again. .. note:: - Why is it called Pulse? Because it's based on a logic called Steel, - and one of the authors and his daughter are big fans of a classic - reggae band called `Steel Pulse `_. We - wanted a name that was softer than Steel, and, well, a bit - playful. So, Pulse! + Why is it called Pulse? Because it grew from a prior logic called + `Steel `_, and one of the + authors and his daughter are big fans of a classic reggae band + called `Steel Pulse `_. We wanted a name + that was softer than Steel, and, well, a bit playful. So, Pulse! diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst index 8156df053e2..59d1b1ee23a 100755 --- a/book/pulse/pulse_atomics_and_invariants.rst +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -74,23 +74,12 @@ computations described by the computation type below: .. code-block:: fstar val stt_atomic (t:Type) (i:inames) (pre:vprop) (post:t -> vprop) - : Type u#2 + : Type u#4 Like ``stt_ghost``, atomic computations are total and live in universe -``u#2``. As such, you cannot store an atomic function in the state, +``u#4``. As such, you cannot store an atomic function in the state, i.e., ``ref (unit -> stt_atomic t i p q)`` is not a well-formed type. -Sometimes, we will also refer to the following computation type: - -.. code-block:: fstar - - val stt_unobservable (t:Type) (i:inames) (pre:vprop) (post:t -> vprop) - : Type u#2 - -Unobservable computations, or ``stt_unobservable``, are very closed -related to ghost computations, though are slightly different -technically---we'll learn more about these shortly. - Atomic computations are also indexed by ``i:inames``, where ``inames`` is a set of invariant names. We'll learn about these next. @@ -101,21 +90,58 @@ In ``Pulse.Lib.Core``, we have the following types: .. code-block:: fstar - val inv (p:vprop) : Type u#0 - val iname : eqtype - val name_of_inv #p (i:inv p) : GTot iname + [@@erasable] + val iref : Type0 + val inv (i:iref) (p:vprop) : vprop -The type ``inv p`` is the type of an *invariant*. Think of ``i:inv p`` -as a *token* which guarantees that ``p`` is true in the current state -and all future states of the program. Every invariant has a name, -``name_of_inv i``, though, the name is only relevant in -specifications, i.e., it is ghost. - -Creating an invariant -+++++++++++++++++++++ +Think of ``inv i p`` as a predicate asserting that ``p`` is true in +the current state and all future states of the program. Every +invariant has a name, ``i:iref``, though, the name is only relevant in +specifications, i.e., it is erasable. + +Boxable predicates +++++++++++++++++++ + +Pulse's language of predicates, i.e., the type ``vprop``, is +stratified. The type ``boxable`` is a refinement of ``vprop``, defined +as shown below in ``Pulse.Lib.Core`` + +.. code-block:: fstar + + let boxable = v:vprop { is_big v } + +That is, certain ``vprops``, i.e., those that satisfy ``is_big``, are +``boxable`` predicates. All the predicates that we have encountered so +far are boxable, except for the ``inv i p`` predicate. For example, +``pts_to x v`` is boxable; ``exists* x. p x`` is boxable if ``p x`` is +boxable; etc. However ``inv i p`` is not boxable. + +Why does this matter? It turns out that PulseCore, the logic on which +Pulse is built, only allows turning boxable predicates into +invariants. That is, while one can build an invariant such as ``inv i +(exists* v. pts_to x v)``, one **cannot** nest invariants, i.e., there +is no meaningful way to construct an instance of ``inv i (inv j p)``. + +This restriction is a fundamental limitation of PulseCore: invariants +cannot mention other invariants. In more technical terms, *invariants +are predicative*. One might wonder whether this limitation is +significant: after all, why might one want to construct an invariant +that states that some ``p`` is already an invariant? It turns out that +such predicates, although not very common, are useful and the +inability to nest invariants in Pulse makes some styles of proofs +awkward or perhaps even impossible. Nevertheless, forcing invariants +to be predicative gives Pulse a simple foundational model in PulseCore +in terms of a standard, predicative, dependently typed logic. + +Let's look next at how to turn a boxable predicate into an invariant. + +Creating an invariant and boxable predicates +++++++++++++++++++++++++++++++++++++++++++++ Let's start by looking at how to create an invariant. + + First, let's define a regular ``vprop``, ``owns x``, to mean that we hold full-permission on ``x``. diff --git a/book/pulse/pulse_ch1.rst b/book/pulse/pulse_ch1.rst index 5f8f8ebac40..2c5bc9a002f 100644 --- a/book/pulse/pulse_ch1.rst +++ b/book/pulse/pulse_ch1.rst @@ -46,7 +46,7 @@ section `, separation logic comes in two parts. **Separation Logic Propositions** First, we have a language of propositions that describe properties about program resources, e.g., the heap. These propositions have the type ``vprop`` in Pulse, [#]_ -and, under the covers in the SteelCore semantics of Pulse, a ``vprop = +and, under the covers in the PulselCore semantics of Pulse, a ``vprop = state -> prop``, where ``state`` represents the state of a program, e.g., the contents of memory. It is useful (at least at first) to think of a ``vprop`` as a memory property, though we will eventually treat @@ -217,8 +217,8 @@ recursively defined predicates, or variants of the connectives described above. .. [#] They are called ``vprop`` for mostly historical reasons. A - version of the Steel separation logic on which Pulse is based - allows associating a *value* with a separation logic + version of the Steel separation logic from which Pulse grew is + based allows associating a *value* with a separation logic proposition, so these came to be known as "value" propositions. However, Pulse does not make use of this feature---perhaps a better name in Pulse would be ``slprop``, diff --git a/book/pulse/pulse_getting_started.rst b/book/pulse/pulse_getting_started.rst index aa7fd7bc6a7..da4aa5bad09 100644 --- a/book/pulse/pulse_getting_started.rst +++ b/book/pulse/pulse_getting_started.rst @@ -26,13 +26,13 @@ the binaries directly in your system. You can find all the source files associated with each chapter `in this folder - `_, + `_, in files named ``PulseTutorial.*.fst``. Creating a Github Codespace ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To do so, go to the `Steel +To do so, go to the `this repository `_ and click on the '<>Code' button, then select 'Create codespace on main'. This will use the Dev Container definition in the `.devcontainer` directory to set up @@ -83,7 +83,7 @@ Using a Pulse release ^^^^^^^^^^^^^^^^^^^^^ A release of Pulse, including related F* tools, `is available here -`_. Uncompress +`_. Uncompress the archive and add follow the instructions in the README.md, notably setting the recommended environment variables. diff --git a/book/pulse/pulse_higher_order.rst b/book/pulse/pulse_higher_order.rst index 9c1c311d7b7..e0234dbde2b 100755 --- a/book/pulse/pulse_higher_order.rst +++ b/book/pulse/pulse_higher_order.rst @@ -80,7 +80,7 @@ below: : Type u#0 val stt_ghost (a:Type u#a) (i:inames) (pre:vprop) (post: a -> vprop) - : Type u#2 + : Type u#4 A point to note is that ``stt`` computations live in universe ``u#0``. This is because ``stt`` computations are allowed to @@ -91,7 +91,7 @@ universe ``u#0``. The universe of ``stt`` means that one can store an is a legal type in Pulse. In contrast, ``stt_ghost`` functions are total and live in -universe 2. You cannot store a ``stt_ghost`` function in the state, +universe 4. You cannot store a ``stt_ghost`` function in the state, since that would allow writing non-terminating functions in ``stt_ghost``. From 2b014f0d58593b89dbdf9256e0e562d6d0081c34 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Thu, 30 May 2024 22:49:40 -0700 Subject: [PATCH 43/65] more on atomics and invariants --- book/pulse/pulse_atomics_and_invariants.rst | 140 ++++++++++++-------- 1 file changed, 87 insertions(+), 53 deletions(-) diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst index 59d1b1ee23a..ff5377324e1 100755 --- a/book/pulse/pulse_atomics_and_invariants.rst +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -80,8 +80,9 @@ Like ``stt_ghost``, atomic computations are total and live in universe ``u#4``. As such, you cannot store an atomic function in the state, i.e., ``ref (unit -> stt_atomic t i p q)`` is not a well-formed type. -Atomic computations are also indexed by ``i:inames``, where ``inames`` -is a set of invariant names. We'll learn about these next. +Atomic computations and ghost computations are also indexed by +``i:inames``, where ``inames`` is a set of invariant names. We'll +learn about these next. Invariants .......... @@ -99,6 +100,26 @@ the current state and all future states of the program. Every invariant has a name, ``i:iref``, though, the name is only relevant in specifications, i.e., it is erasable. +A closely related type is ``iname``: + +.. code-block:: fstar + + val iname : eqtype + let inames = erased (FStar.Set.set iname) + +Every ``iref`` can be turned into an ``iname``, with the function +``iname_of (i:iref): GTot iname``. + +Invariants are duplicable, i.e., from ``inv i p`` one can prove ``inv +i p ** inv i p``, as shown by type of ``Pulse.Lib.Core.dup_inv`` +below: + +.. code-block:: fstar + + val dup_inv (i:iref) (p:vprop) + : stt_ghost unit emp_inames (inv i p) (fun _ -> inv i p ** inv i p) + + Boxable predicates ++++++++++++++++++ @@ -140,66 +161,74 @@ Creating an invariant and boxable predicates Let's start by looking at how to create an invariant. - - -First, let's define a regular ``vprop``, ``owns x``, to mean that we -hold full-permission on ``x``. +First, let's define a predicate ``owns x``, to mean that we hold +full-permission on ``x``. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: fstar :start-after: //owns$ :end-before: //owns$ -Now, if we can currently prove ``pts_to r x`` then we can turn it -into an invariant ``i:inv (owns r)``, as shown below. +Notice the type annotation on ``owns`` claims that it is ``boxable``, +and indeed F*'s refinement type checker automatically proves that it +is. + +Now, if we can currently prove ``pts_to r x`` then we can turn it into +an invariant ``inv i (owns r)``, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: pulse :start-after: //create_invariant$ :end-before: ``` -Importantly, when we turn ``pts_to r x`` into ``inv (owns r)``, **we -lose** ownership of ``pts_to r x``. Remember, once we have ``inv (owns -r)``, Pulse's logic aims to prove that ``owns r`` remains true always. If we -were allowed to retain ``pts_to r x``, while also creating an ``inv -(owns r)``, we can clearly break the invariant, e.g., by freeing -``r``. +Importantly, when we turn ``pts_to r x`` into ``inv i (owns r)``, **we +lose** ownership of ``pts_to r x``. Remember, once we have ``inv i +(owns r)``, Pulse's logic aims to prove that ``owns r`` remains true +always. If we were allowed to retain ``pts_to r x``, while also +creating an ``inv i (owns r)``, we can clearly break the invariant, +e.g., by freeing ``r``. .. note:: - A tip: When using an ``inv p``, it's a good idea to make sure that - ``p`` is a user-defined predicate. For example, one might think to - just write ``inv (exists* v. pts_to x v)`` instead of defining an - auxiliary predicate for ``inv (owns r)``. However, the some of the - proof obligations produced by the Pulse checker are harder for the - SMT solver to prove if you don't use the auxiliary predicate and - you may start to see odd failures. This is something we're working - to improve. In the meantime, use an auxiliary predicate. + A tip: When using an ``inv i p``, it's a good idea to make sure + that ``p`` is a user-defined predicate. For example, one might + think to just write ``inv i (exists* v. pts_to x v)`` instead of + defining an auxiliary predicate for ``inv i (owns r)``. However, the + some of the proof obligations produced by the Pulse checker are + harder for the SMT solver to prove if you don't use the auxiliary + predicate and you may start to see odd failures. This is something + we're working to improve. In the meantime, use an auxiliary + predicate. -``new_invariant`` is unobservable -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Note, if one were to try to allocate an invariant for a non-boxable predicate, +typechecking fails, as shown in the example below: -The type of ``new_invariant`` is shown below: +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //create_non_boxable$ + :end-before: ``` -.. code-block:: fstar +failing with an error pointing to the source location of the +refinement precondition, ``is_big``, at the call to ``new_invariant``. - val new_invariant (p:vprop) - : stt_unobservable (inv p) emp_inames p (fun _ -> emp) +.. code-block:: -The ``stt_unobservable`` says that ``new_invariant`` is an atomic step -of computation from Pulse's perspective, but it doesn't read or change -any observable state. In that regard, ``stt_unobservable`` is a lot -like ``stt_ghost``; however, while ``stt_ghost`` computations are -allowed to use F* ghost operations like ``reveal : erased a -> GTot -a``, unobservable computations are not. + - Assertion failed + - The SMT solver could not prove the query. Use --query_stats for more details. + - See also ../../../lib/pulse/lib/Pulse.Lib.Core.fsti(536,29-536,37) -A ``stt_ghost`` computation with a non-informative result can be -lifted to ``stt_unobservable``. +As you can see, although the language does not prevent you from +writing ``inv i p`` for any predicate ``p``, the only way to allocate +an instance of ``inv i p`` is by provable that ``p`` is +``boxable``. This design is convenient since the onus of proving that +a predicate is boxable is only placed at the allocation site of the +invariant---uses of invariants do not need to worry about the +distinction between ``boxable`` and general ``vprops``. Opening an invariant ++++++++++++++++++++ -Now that we've allocated an ``inv (owns r)``, what can we do with it? +Now that we've allocated an ``inv i (owns r)``, what can we do with it? As we said earlier, one can make use of the ``owns r`` in an atomic computation, so long as we restore it at the end of the atomic step. @@ -235,39 +264,44 @@ This is syntactic sugar for the following nest: ... } -Here's the rule for opening a single invariant ``i:inv p`` using +Here's the rule for opening a single invariant ``inv i p`` using ``with_invariant i { e }`` is as follows: -* ``i`` must have type ``inv p``, for some ``p:vprop`` +* ``i`` must have type ``iref`` and ``inv i p`` must be provable in + the current context, for some ``p:vprop`` * ``e`` must have the type ``stt_atomic t j (p ** r) (fun x -> p ** s x)``. [#]_ That is, ``e`` requires and restores the invariant ``p``, while also transforming ``r`` to ``s x``, all in at most one atomic step. Further, the ``name_of_inv i`` must not be in the set ``j``. -* ``with_invariants i { e }`` has type ``stt_atomic t (add_inv i j) r - s``. That is, ``e`` gets to use ``p`` for a step, and from the - caller's perspective, the context was transformed from ``r`` to - ``s``, while the use of ``p`` is hidden. +* ``with_invariants i { e }`` has type ``stt_atomic t (add_inv i j) + (inv i p ** r) (fun x -> inv i p ** s x)``. That is, ``e`` gets to + use ``p`` for a step, and from the caller's perspective, the context + was transformed from ``r`` to ``s``, while the use of ``p`` is + hidden. * Pay attention to the ``add_inv i j`` index on ``with_invariants``: - ``stt_atomic`` (or ``stt_unobservable``) computation is indexed by + ``stt_atomic`` (or ``stt_ghost``) computation is indexed by the names of all the invariants that it may open. + Let's look at a few examples to see how ``with_invariants`` works. .. [#] - Note, ``e`` may also have type ``stt_unobservable t j (p ** r) (fun - x -> p ** s x)``, in which case ``with_invariant i { e }`` has type - ``stt_unobservable t (add_inv i j) r s``. - + Alternatively ``e`` may have type ``stt_ghost t j (p ** r) (fun x + -> p ** s x)``, in which case the entire ``with_invariants i { e + }`` block has type ``stt_ghost t (add_inv i j) (inv i p ** r) (fun + x -> inv i p ** s x)``, i.e., one can open an invariant and use it + in either an atomic or ghost context. + Updating a reference ~~~~~~~~~~~~~~~~~~~~ -In the example below, given ``inv (owns r)``, we can atomically update -a reference with a pre- and postcondition of ``emp``. +In the example below, given ``inv i (owns r)``, we can atomically +update a reference with a pre- and postcondition of ``emp``. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: pulse @@ -289,9 +323,9 @@ a reference with a pre- and postcondition of ``emp``. * Since we opened the invariant ``i``, the type of ``update_ref_atomic`` records this in the ``opens (singleton i)`` annotation; equivalently, the type is ``stt_atomic unit - (singleton i) emp (fun _ -> emp)``. When the ``opens`` annotation is - omitted, it defaults to ``emp_inames``, the empty set of invariant - names. + (singleton i) (inv i (owns r)) (fun _ -> inv i (owns r))``. When the + ``opens`` annotation is omitted, it defaults to ``emp_inames``, the + empty set of invariant names. Double opening is unsound ~~~~~~~~~~~~~~~~~~~~~~~~~ From a3f283521af8bbf2eaa1cff674d12f24219b7d90 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 2 Jun 2024 11:22:47 -0700 Subject: [PATCH 44/65] update chapters on spin lock, parallel increment, and extraction --- book/pulse/pulse_extraction.rst | 22 ++-- book/pulse/pulse_parallel_increment.rst | 128 +++++++++++++++--------- book/pulse/pulse_spin_lock.rst | 78 ++++++++++----- 3 files changed, 141 insertions(+), 87 deletions(-) diff --git a/book/pulse/pulse_extraction.rst b/book/pulse/pulse_extraction.rst index e31088167d5..04797e58903 100644 --- a/book/pulse/pulse_extraction.rst +++ b/book/pulse/pulse_extraction.rst @@ -75,13 +75,13 @@ the output is written to ``stdout``, if an ``-o `` option is provided to t the output is written to ``file``). For example, the first command produces the ``.ast`` file from ``PulseTutorial.Algorithms.fst`` (which contains the Boyer-Moore algorithm implementation), and then the second command extracts the Rust code to ``voting.rs``. (These commands are run in the -``steel`` root directory, change the location of main.exe according to your setup.) +``pulse`` root directory, change the location of main.exe according to your setup.) .. code-block:: shell - $ fstar.exe --include lib/steel/ --include lib/steel/pulse --include share/steel/examples/pulse/lib/ - --include share/steel/examples/pulse/by-example/ --include share/steel/examples/pulse/_output/cache/ - --load_cmxs steel --odir . PulseTutorial.Algorithms.fst + $ fstar.exe --include lib/pulse/ --include lib/pulse/lib + --include share/pulse/examples/by-example/ --include share/pulse/examples/_output/cache/ + --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst --extract 'PulseTutorial.Algorithms' --codegen Extension $ ./pulse2rust/main.exe PulseTutorial_Algorithms.ast -o voting.rs @@ -137,13 +137,13 @@ that internally calls the polymorphic ``majority`` function: :start-after: //majoritymono$ :end-before: //majoritymonoend$ -Then we extract it to C as follows (the commands are run in the ``steel`` root directory as before): +Then we extract it to C as follows (the commands are run in the ``pulse`` root directory as before): .. code-block:: shell - $ fstar.exe --include lib/steel/ --include lib/steel/pulse --include share/steel/examples/pulse/lib/ - --include share/steel/examples/pulse/by-example/ --include share/steel/examples/pulse/_output/cache/ - --load_cmxs steel --odir . PulseTutorial.Algorithms.fst + $ fstar.exe --include lib/pulse/ --include lib/pulse/lib + --include share/pulse/examples/by-example/ --include share/pulse/examples/_output/cache/ + --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst --extract 'FStar.Pervasives.Native PulseTutorial.Algorithms' --codegen krml $ ../karamel/krml -skip-compilation out.krml @@ -185,9 +185,9 @@ For the Boyer-Moore example, we can extract the program to OCaml as follows: .. code-block:: shell - $ fstar.exe --include lib/steel/ --include lib/steel/pulse --include share/steel/examples/pulse/lib/ - --include share/steel/examples/pulse/by-example/ --include share/steel/examples/pulse/_output/cache/ - --load_cmxs steel --odir . PulseTutorial.Algorithms.fst + $ fstar.exe --include lib/pulse/ --include lib/pulse/lib + --include share/pulse/examples/by-example/ --include share/pulse/examples/_output/cache/ + --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst --extract 'PulseTutorial.Algorithms' --codegen OCaml and the extracted ``majority`` function looks like: diff --git a/book/pulse/pulse_parallel_increment.rst b/book/pulse/pulse_parallel_increment.rst index f192f7f681e..d5e39a58b28 100755 --- a/book/pulse/pulse_parallel_increment.rst +++ b/book/pulse/pulse_parallel_increment.rst @@ -111,7 +111,22 @@ To enforce synchronization, we could use a lock, e.g., shown below: This program is type correct and free from data races. But, since the lock holds the entire permission on ``x``, there's no way to give this -function a meaningful postcondition, aside from ``emp``. +function a precise postcondition. + +.. note :: + + In this section, we use an implementation of spin locks from the + Pulse library, Pulse.Lib.SpinLock. Unlike the version we developed + in the previous chapter, these locks use a fraction-indexed + permission, ``lock_alive l #f p``. The also provide a predicate, + ``lock_acquired l``, that indicates when the lock has been + taken. With full-permission to the lock, and ``lock_acquired l``, + the lock can be freed---reclaiming the underlying + memory. Additionally, the ``lock_acquired`` predicate ensures that + locks cannot be double freed. As such, ``Pulse.Lib.SpinLock`` fixes + the problems with the spin locks we introduced in the previous + chapter and also provides a solution to the exercises given there. + A First Take, with Locks ........................ @@ -216,11 +231,6 @@ Finally, we can implement ``add2`` with the specification we want: left right i v`` tells us that ``v == i + 1 + 1``, which is what we need to conclude. -This version also has the problem that we allocate lock and then -acquire it at the end, effectively leaking the memory associated with -the lock. We'll see how to fix that below. - - Modularity with higher-order ghost code ....................................... @@ -232,7 +242,7 @@ idea proposed by Bart Jacobs and Frank Piessens in a paper titled `_. The main idea is to observe that ``incr_left`` and ``incr_right`` only -deferred by the ghost code that they execute. But, Pulse is higher +differ by the ghost code that they execute. But, Pulse is higher order: so, why not parameterize a single function by ``incr`` and let the caller instantiate ``incr`` twice, with different bits of ghost code. Also, while we're at it, why not also generalize the @@ -263,7 +273,7 @@ it is parameterized by: Having generalized ``incr``, we've now shifted the work to the caller. But, ``incr``, now verified once and for all, can be used with -many different callers just by instantiating it difference. For +many different callers just by instantiating it differently. For example, if we wanted to do a three-way parallel increment, we could reuse our ``incr`` as is. Whereas, our first take would have to be completely revised, since ``incr_left`` and ``incr_right`` assume that @@ -290,8 +300,8 @@ This code still has two issues: state using Pulse's support for partial commutative monoids---but that's for another chapter. -* We still leak the memory associated with the lock at the end---we'll - remedy that next. +* We allocate and free memory for a lock, which is inefficient---could + we instead do things with atomic operations? We'll remedy that next. Exercise ++++++++ @@ -320,55 +330,75 @@ problem of adapting this to ``U32.t``. Cancellable Invariants ++++++++++++++++++++++ -The main idea of doing the ``add2`` proof is to use an invariant token -``i:inv p`` instead of a ``l:lock p``. Just as in our previous code, -``add2`` starts with allocating an invariant, putting -``exists* v. pts_to x v ** contribution left right i v`` in the -invariant. Then call incr twice in different threads. However, -finally, to recover ``pts_to x (v + 2)``, where previously we would -acquire the lock, with a regular invariant, we're stuck, since the -``pts_to x v`` permission is inside the invariant and we can't take it -out to return to the caller. - -An invariant token ``i:inv p`` is a witness that the property ``p`` is -true and remains true for the rest of a program's execution. But, what -if we wanted to only enforce ``p`` as an invariant for some finite +The main idea of doing the ``add2`` proof is to use an invariant +instead of a lock. Just as in our previous code, ``add2`` starts with +allocating an invariant, putting ``exists* v. pts_to x v ** +contribution left right i v`` in the invariant. Then call incr twice +in different threads. However, finally, to recover ``pts_to x (v + +2)``, where previously we would acquire the lock, with a regular +invariant, we're stuck, since the ``pts_to x v`` permission is inside +the invariant and we can't take it out to return to the caller. + +An invariant ``inv i p`` guarantees that the property ``p`` is true +and remains true for the rest of a program's execution. But, what if +we wanted to only enforce ``p`` as an invariant for some finite duration, and then to cancel it? This is what the library -``Pulse.Lib.CancellableInvariant`` provides. By allocating a ``i:inv -(cancellable t p)``, we get an invariant which enforces ``p`` only so -long as ``active perm t`` is also provable. Here's the relevant part +``Pulse.Lib.CancellableInvariant`` provides. Here's the relevant part of the API: .. code-block:: pulse + [@@ erasable] + val cinv : Type0 + val iref_of (c:cinv) : GTot iref + +The main type it offers is ``cinv``, the name of a cancellable +invariant. + + +.. code-block:: pulse + ghost - fn create (v:vprop) + fn new_cancellable_invariant (v:boxable) requires v - returns t:token - ensures cancellable t v ** active 1.0R t - + returns c:cinv + ensures inv (iref_of c) (cinv_vp c v) ** active c 1.0R + +Allocating a cancellable invariant is similar to allocating a regular +invariant, except one gets an invariant for an abstract predicate +``cinv_cp c v``, and a fraction-indexed predicate ``active c 1.0R`` +which allows the cancellable invariant to be shared and gathered +between threads. + +The ``cinv_cp c v`` predicate can be used in conjunction with +``active`` to recover the underlying predicate ``v``---but only when +the invariant has not been cancelled yet---this is what +``unpack_cinv_vp``, and its inverse, ``pack_cinv_vp``, allow one to +do. + +.. code-block:: pulse + ghost - fn take (#p #t:_) (v:vprop) - requires cancellable t v ** active p t - ensures v ** active p t ** active 1.0R t + fn unpack_cinv_vp (#p:perm) (#v:vprop) (c:cinv) + requires cinv_vp c v ** active c p + ensures v ** unpacked c ** active c p ghost - fn restore (#t:_) (v:vprop) - requires v ** active 1.0R t - ensures cancellable t v + fn pack_cinv_vp (#v:vprop) (c:cinv) + requires v ** unpacked c + ensures cinv_vp c v + +Finally, if one has full permission to the invariant (``active c +1.0R``) it can be cancelled and the underlying predicate ``v`` can be +obtained as postcondition. - fn cancel_inv (#t #v:_) (i:inv (cancellable t v)) - requires active 1.0R t +.. code-block:: pulse + + ghost + fn cancel (#v:vprop) (c:cinv) + requires inv (iref_of c) (cinv_vp c v) ** active c 1.0R ensures v - -A ``cancellable t v`` is created from a proof of ``v``, also providing -a fractional-permission indexed predicate ``active 1.0R t``, -which can be shared and gathered as usual. So long as one can prove -that the token ``t`` is active, one can call ``take`` to trade a -``cancellable t v`` for ``v``, and vice-versa. Finally, with full -permission on the ``active`` predicate, ``cancel_inv`` is a utility to -stop enforcing ``v`` an invariant and to extract it from a ``i:inv -(cancellable t v)``. + opens add_inv emp_inames (iref_of c) An increment operation ++++++++++++++++++++++ @@ -397,8 +427,8 @@ The ``read`` function is relatively easy: :end-before: //incr_atomic_body_read$ * We open the invariant ``l``; then, knowing that the invariant is - still active, we can ``take`` it; then read the value ``v``; - ``restore`` the invariant; and return ``v``. + still active, we can unpack` it; then read the value ``v``; pack it + back; and return ``v``. The main loop of ``incr_atomic`` is next, shown below: diff --git a/book/pulse/pulse_spin_lock.rst b/book/pulse/pulse_spin_lock.rst index f044eacb656..f8b6c77fce9 100755 --- a/book/pulse/pulse_spin_lock.rst +++ b/book/pulse/pulse_spin_lock.rst @@ -12,18 +12,18 @@ Representing a Lock The main idea of the implementation is to represent a lock using a mutable machine word, where the value ``0ul`` signifies that the lock -is currently released; and ``1ul`` signifies the the lock is currently +is currently released; and ``1ul`` signifies that the lock is currently acquired. To acquire a lock, we'll try to atomically compare-and-swap, repeating until we succeed in setting a ``1ul`` and acquiring the lock. Releasing the lock is simpler: we'll just set it to ``0ul`` (though we'll explore a subtlety on how to handle double releases). From a specification perspective, a lock is lot like an invariant: the -type ``lock p`` is a kind of token that states that the lock protects -some property ``p``. Acquiring the lock provides ``p`` to the caller; -while releasing the lock requires the caller to give up ownership of +predicate type ``lock_alive l p`` states that the lock protects some +property ``p``. Acquiring the lock provides ``p`` to the caller; while +releasing the lock requires the caller to give up ownership of ``p``. The runtime mutual exclusion is enforced by the acquire -spinning, or looping, until the lock is available. +spinning, or looping, until the lock is available. We'll represent a lock as a pair of reference to a ``U32.t`` and an invariant: @@ -33,38 +33,61 @@ invariant: :start-after: //lock$ :end-before: //lock$ -The invariant states: +The predicate ``lock_inv r p`` states: - * We hold full permission to the ``r:ref U32``; and + * We hold full permission to the ``r:box U32.t``; and * If ``r`` contains ``0ul``, then we also have ``p``. +The lock itself pairs the concrete mutable state ``box U32.t`` with an +invariant reference ``i:iref``, where the ``lock_alive l p`` predicate +states that ``l.i`` names an invariant for ``lock_alive l.r p``. + + Creating a lock ............... To create a lock, we implement ``new_lock`` below. It requires the caller to provide ``p``, ceding ownership of ``p`` to the newly -allocated ``l:lock p`` +allocated ``l:lock`` .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst :language: pulse :start-after: //new_lock$ :end-before: ``` +Importantly, since allocating a lock involves allocating an invariant +that protects the predicate ``p``, we need ``p`` to be ``boxable``. + Some notes on the implementation: * We heap allocate a reference using ``Box.alloc``, since clearly, the lock has to live beyond the scope of this function's activation. -* We use ``new_invariant`` to create an ``inv (lock_inv _)``, and +* We use ``new_invariant`` to create an ``inv i (lock_inv r p)``, and package it up with the newly allocated reference. + +Duplicating permission to a lock +................................ + +Locks are useful only if they can be shared between multiple +threads. The ``lock_alive l p`` expresses ownership of a lock---but, +since ``lock_alive`` is just an invariant, we can use ``dup_inv`` to +duplicate ``lock_alive``. + +.. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst + :language: pulse + :start-after: //dup_lock_alive$ + :end-before: ``` + + Acquiring a lock ................ -The signature of ``acquire`` is shown below: it's says that with -``l:lock p``, we can get back ``p`` without proving anything, i.e., -the precondition is ``emp``. +The signature of ``acquire`` is shown below: it says that with +``lock_alive l p``, we can get back ``p`` without proving anything, +i.e., the precondition is ``emp``. .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst :language: pulse @@ -87,18 +110,18 @@ Pulse.Lib.Reference: .. code-block:: pulse atomic - fn cas (r:ref U32.t) (u v:U32.t) (#i:erased U32.t) - requires pts_to r i + fn cas_box (r:Box.box U32.t) (u v:U32.t) (#i:erased U32.t) + requires Box.pts_to r i returns b:bool - ensures cond b (pts_to r v ** pure (reveal i == u)) - (pts_to r i)) + ensures cond b (Box.pts_to r v ** pure (reveal i == u)) + (Box.pts_to r i)) -The specification of ``cas r u v`` says that we can try to atomically +The specification of ``cas_box r u v`` says that we can try to atomically update ``r`` from ``u`` to ``v``, and if the operation succeeds, we learn that the initial value (``i``) of ``r`` was equal to ``u``. -Using ``cas``, we can implement ``acquire`` using a tail-recursive +Using ``cas_box``, we can implement ``acquire`` using a tail-recursive function: .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst @@ -108,22 +131,23 @@ function: The main part of the implementation is the ``with_invariants`` block. -* Its return type ``b:bool`` and postcondition is ``maybe b p``, - signifying that after a single ``cas``, we may have ``p`` if the - ``cas`` succeeded. +* Its return type ``b:bool`` and postcondition is ``inv l.i (lock_inv + l.r p) ** maybe b p``, signifying that after a single ``cas``, we + may have ``p`` if the ``cas`` succeeded, while maintaining the invariant. -* We open ``l.i`` to get ``lock_inv``, and then try a ``cas l.r 0ul 1ul`` +* We open ``l.i`` to get ``lock_inv``, and then try a ``cas_box l.r 0ul 1ul`` -* If the ``cas`` succeeds, we know that the lock was initially in the - ``0ul`` state. So, from ``lock_inv`` we have ``p``, and we can "take - it out" of the lock and return it out of block as ``maybe true +* If the ``cas_box`` succeeds, we know that the lock was initially in + the ``0ul`` state. So, from ``lock_inv`` we have ``p``, and we can + "take it out" of the lock and return it out of block as ``maybe true p``. And, importantly, we can trivially restore the ``lock_inv``, since we know its currently value is ``1ul``, i.e., ``maybe (1ul = 0ul) _ == emp``. -* If the ``cas`` fails, we just restore ``lock_inv`` and return false. +* If the ``cas_box`` fails, we just restore ``lock_inv`` and return + false. -Outside the ``with_invariants`` block, if the cas succeeded, then +Outside the ``with_invariants`` block, if the CAS succeeded, then we're done: we have ``p`` to return to the caller. Otherwise, we recurse and try again. From 1e578673083205d3129bffb7ab1f15d9efdb95e5 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Tue, 25 Jun 2024 22:06:29 -0700 Subject: [PATCH 45/65] fix up some docs to reflect improvements in the Pulse checker --- README.md | 31 +++++++++++++++++++++++++++++++ book/Makefile | 4 ++-- book/pulse/pulse_ghost.rst | 32 -------------------------------- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 37dde66d70a..104fc4696c3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ # PoP-in-FStar The Proof-oriented Programming in F* Book + + +# Build + +To set up an environment to build the book, you will need python3, sphinx, and the sphinx_rtd_theme. + +Once you have python3, on Ubuntu-24, install using: + +* sudo apt install python3-sphinx +* sudo apt install python3-sphinx_rtd_theme + +Then, see book/Makefile: + +- set FSTAR_HOME, if the default is not correct +- likewise, set PULSE_HOME + +Then, + +* make -C book prep #to copy source files from F* and Pulse into the book build folders +* make -C book html +* make -C book pdf + +You should have book/_build/html/index.html + + +# Deploy + + +Set FSTARLANG_ORG_ROOT in book/Makefile + +* make deploy diff --git a/book/Makefile b/book/Makefile index 69d31d64d6d..7c46bcaa4f3 100644 --- a/book/Makefile +++ b/book/Makefile @@ -1,7 +1,7 @@ # Minimal makefile for Sphinx documentation # -export FSTAR_HOME?=$(realpath ../..) +export FSTAR_HOME?=$(realpath ../../everest/FStar) export PULSE_HOME?=$(FSTAR_HOME)/../pulse FSTARLANG_ORG_ROOT?=www @@ -51,5 +51,5 @@ deploy: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile +%: Makefil @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/book/pulse/pulse_ghost.rst b/book/pulse/pulse_ghost.rst index 19b56ca55f5..fb8d6e82a0e 100755 --- a/book/pulse/pulse_ghost.rst +++ b/book/pulse/pulse_ghost.rst @@ -191,38 +191,6 @@ As we did when working with :ref:`nullable references ghost functions to introduce and eliminate this predicate, for each of its cases. -The elimination and introduction functions for the ``[]`` case are -very similar to what had previously with nullable references: - - -.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst - :language: pulse - :start-after: //intro_all_at_most_nil$ - :end-before: ``` - -.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst - :language: pulse - :start-after: //elim_all_at_most_nil$ - :end-before: ``` - -For the ``hd::tl`` case, we have to be a bit more careful in how we do -the rewriting: using a regular ``rewrite`` fails, since it involves -proving an equality between two higher-order terms (the body of -``exists*`` is represented in F* as a lambda term). Pulse also -provides a ``rewrite_by`` function in the library that allows -rewriting terms using a user-provided tactic. In this case, the tactic -``Pulse.Lib.Pervasives.vprop_equiv_norm`` equates ``vprops`` by -proving them definitionally equal by normalization. - -.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst - :language: pulse - :start-after: //intro_all_at_most_cons$ - :end-before: ``` - -.. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst - :language: pulse - :start-after: //elim_all_at_most_cons$ - :end-before: ``` Recursive Ghost Lemmas ++++++++++++++++++++++ From ae285fbfb6c90083c29c57a81684c6b179c24d72 Mon Sep 17 00:00:00 2001 From: Robert Peterson Date: Mon, 1 Jul 2024 10:59:53 -0700 Subject: [PATCH 46/65] fix grammar --- book/intro.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/intro.rst b/book/intro.rst index 8a3d0a50dfd..6f0c8973883 100644 --- a/book/intro.rst +++ b/book/intro.rst @@ -223,7 +223,7 @@ F* proofs use SMT solving, symbolic computation and tactics ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Stating a theorem or lemma in F* amounts to declaring a type signature -and a doing a proof corresponds to providing an implementation of that +and doing a proof corresponds to providing an implementation of that signature. Proving theorems can take a fair bit of work by a human and F* seeks to reduce that burden, using a variety of techniques. From 98db633127ff5d419d0788973f0c2f94094bc33c Mon Sep 17 00:00:00 2001 From: Robert Peterson Date: Thu, 4 Jul 2024 22:49:16 -0700 Subject: [PATCH 47/65] fix grammar --- book/part1/part1_inductives.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part1/part1_inductives.rst b/book/part1/part1_inductives.rst index 751bbfb583c..0914fb6097c 100644 --- a/book/part1/part1_inductives.rst +++ b/book/part1/part1_inductives.rst @@ -307,7 +307,7 @@ int { same_case z x }``. Lists ^^^^^ -All the types we've see far have been inductive only in a degenerate +All the types we've seen so far have been inductive only in a degenerate sense—the constructors do not refer to the types they construct. Now, for our first truly inductive type, a list. From 5de5fe467c1dcd0580422b5c9c7ab674decc86b4 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sat, 6 Jul 2024 12:25:14 -0700 Subject: [PATCH 48/65] rewrite www.fstar-lang.org to fstar-lang.org --- book/notes | 4 ++-- book/part4/part4_background.rst | 4 ++-- book/pulse/pulse.rst | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/book/notes b/book/notes index f8f4e2d05be..f97b42411e8 100644 --- a/book/notes +++ b/book/notes @@ -209,7 +209,7 @@ effect system. This tutorial provides a first taste of verified programming in F\*. More information about F\*, including papers and technical reports, can be found on the `F\* website -`_. +`_. It will help if you are already familiar with functional programming languages in the ML family (e.g., [OCaml], [F#], [Standard ML]), or @@ -255,7 +255,7 @@ exercise. Your progress on each exercise will be stored in browser local storage, so you can return to your code later (e.g. if your browser crashes, etc). -[online F\* editor]: https://www.fstar-lang.org/run.php +[online F\* editor]: https://fstar-lang.org/run.php You can also do this tutorial by installing F\* locally on your machine. F\* is open source and cross-platform, and you can get diff --git a/book/part4/part4_background.rst b/book/part4/part4_background.rst index c980bc29ab7..585cb35bbdd 100644 --- a/book/part4/part4_background.rst +++ b/book/part4/part4_background.rst @@ -71,7 +71,7 @@ of these papers throughout this part of the book. inference of weakest preconditions of effectful computations. + This 2016 paper, - `Dependent Types and Multi-Monadic Effects in F* `_, + `Dependent Types and Multi-Monadic Effects in F* `_, has become the canonical reference for F\*. It shows how to combine multiple Dijkstra monads with a DCC-like system. @@ -87,7 +87,7 @@ of these papers throughout this part of the book. monad morphism. + `Programming and Proving with Indexed Effects - `_ describes + `_ describes F*'s user-defined effect system in its most general form, allowing it to be applied to any indexed effects, including Dijkstra monads, but several other constructions as well. diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 623851623a5..05a8d837a42 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -62,7 +62,7 @@ etc. will be of use again. .. note:: Why is it called Pulse? Because it grew from a prior logic called - `Steel `_, and one of the + `Steel `_, and one of the authors and his daughter are big fans of a classic reggae band called `Steel Pulse `_. We wanted a name that was softer than Steel, and, well, a bit playful. So, Pulse! From 12d839649abce6337601ed0af94a47ed53bcd16d Mon Sep 17 00:00:00 2001 From: Jestine Paul Date: Wed, 7 Aug 2024 17:37:58 +0800 Subject: [PATCH 49/65] Update part1_termination.rst --- book/part1/part1_termination.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part1/part1_termination.rst b/book/part1/part1_termination.rst index 373b4aaaf27..f8b0fcae26d 100644 --- a/book/part1/part1_termination.rst +++ b/book/part1/part1_termination.rst @@ -106,7 +106,7 @@ fully, it would be as shown below: : Tot nat (decreases l) = match l with | [] -> 0 - | _ :: tl -> length tl + | _ :: tl -> 1 + length tl The main difference is on the second line. As opposed to just writing the result type of ``length``, in full detail, we write From 1577ab0789c0994f35c75312bbbb5b9c0f9e38a5 Mon Sep 17 00:00:00 2001 From: Gabriel Ebner Date: Wed, 8 Jan 2025 10:35:17 -0800 Subject: [PATCH 50/65] Remove pull. --- book/part4/part4_ghost.rst | 62 +++----------------------------------- 1 file changed, 4 insertions(+), 58 deletions(-) diff --git a/book/part4/part4_ghost.rst b/book/part4/part4_ghost.rst index 0cd5c61e340..f2e86c8a4e3 100644 --- a/book/part4/part4_ghost.rst +++ b/book/part4/part4_ghost.rst @@ -368,8 +368,10 @@ Using Ghost Computations in Total Contexts We have already noted that ``Tot < GTot``, enabling ``Tot`` computations to be re-used in ``GTot`` contexts. For erasure to be sound, it is crucial that ``GTot`` terms cannot be used in ``Tot`` -contexts, and indeed, F* forbids this in general. However, there are -two exceptions. +contexts, and indeed, F* forbids this in general. +However, there is one exception where +we can directly invoke a ``GTot`` computation in a ``Tot`` context +without wrapping the result in ``Ghost.erased``. Effect Promotion for Non-informative Types @@ -415,62 +417,6 @@ in the ``Tot`` effect. :end-before: //SNIPPET_END: factorial_tail_alt$ -FStar.Ghost.Pull.pull -..................... - -Consider applying ``map factorial [0;1;2]``, where ``map : (a -> -Tot b) -> list a -> list b``. F* refuses to typecheck this written, -since ``factorial : nat -> GTot nat``, which is not a subtype of ``nat --> Tot nat``. - -Using effect promotion, one could write ``map (fun x -> hide -(factorial x)) [0;1;2]``, but this has type ``list (erased nat)``, -which is not the same as ``list nat``. - -This is unfortunate since it seems that basic libraries designed for -use with with higher-order total functions cannot be reused with ghost -functions. [#]_ - -The library function ``FStar.Ghost.Pull.pull`` can help in such -situatinos. It has the following signature: - -.. code-block:: fstar - - val pull (#a:Type) (#b:a -> Type) (f: (x:a -> GTot (b x))) - : GTot (x:a -> b x) - - val pull_equiv (#a:Type) (#b:a -> Type) (f: (x:a -> GTot (b x))) (x:a) - : Lemma (ensures pull f x == f x) - [SMTPat (pull f x)] - - -This type states that for any ghost function ``f``, we can exhibit a -total function ``g`` with the same type as ``f``, while the lemma -``pull_equiv`` states that ``g`` is pointwise equal to ``f``. [#]_ However, -it may not be possible, in general, to compute ``g`` in a way that -enables it to be compiled by F*. So, ``pull f`` itself has ghost -effect, indicating that applications of ``pull`` cannot be used in -compilable code. - -Using ``pull``, one can write ``map (pull factorial) [0;1;2] : GTot -(list nat)``, thereby reusing ghost functions where total functions -are expected. - -.. [#] F* does not have any built-in support for effect-label - polymorphism. That is, one cannot write a type like this: ``map - #a #b #e (f: a -> e b) (l:list a) : e (list b)``, where ``e`` - is an effect variable. However, using F*'s dependent types, one - can code up various `ad hoc forms of effect polymorphism - <../code/AdHocEffectPolymorphism.fst>`_. Some other languages - with effect systems, notably `Koka - `_, do support - effect polymorphism primitively. - -.. [#] The ``SMTPat`` annotation on the ``pull_equiv`` lemma instructs - F* to allow Z3 to instantiate this lemma automatically. You - can learn more :ref:`about SMT patterns and quantifier - instantiation in a later chapter `. - Revisiting Vector Concatenation ------------------------------- From adc050458eaf60ac1fc52099ee8650a9fb2abe08 Mon Sep 17 00:00:00 2001 From: Wolfgang Meyer Date: Sun, 12 Jan 2025 22:21:00 +0100 Subject: [PATCH 51/65] Minor fix (Obviously, F# was intended here) --- book/part2/part2_vectors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part2/part2_vectors.rst b/book/part2/part2_vectors.rst index c900c64ac7f..4bd9a429430 100644 --- a/book/part2/part2_vectors.rst +++ b/book/part2/part2_vectors.rst @@ -119,7 +119,7 @@ different indexes (``0`` and ``n + 1``). construct the same instance of the type constructor. Further, all data constructors take at most one argument. If your datatype happens to be simple enough to fit these restrictions, you can use - a notation similar to OCaml or F* for those types in F*. For + a notation similar to OCaml or F# for those types in F*. For example, here's the ``option`` type defined in F* using an OCaml-like notation. From e950bb1c1cb8f6ce5aa4043a10438271dc33450b Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 6 Apr 2025 15:36:09 -0700 Subject: [PATCH 52/65] update references to code snippets, migrated to #lang-pulse --- book/pulse/pulse.rst | 8 +- book/pulse/pulse_arrays.rst | 18 ++-- book/pulse/pulse_atomics_and_invariants.rst | 16 ++-- book/pulse/pulse_ch1.rst | 30 ++++--- book/pulse/pulse_ch2.rst | 88 +++++++++----------- book/pulse/pulse_conditionals.rst | 36 ++++---- book/pulse/pulse_existentials.rst | 24 +++--- book/pulse/pulse_ghost.rst | 32 +++---- book/pulse/pulse_higher_order.rst | 10 +-- book/pulse/pulse_implication_and_forall.rst | 32 +++---- book/pulse/pulse_linked_list.rst | 28 +++---- book/pulse/pulse_loops.rst | 30 +++---- book/pulse/pulse_parallel_increment.rst | 42 +++++----- book/pulse/pulse_spin_lock.rst | 14 ++-- book/pulse/pulse_user_defined_predicates.rst | 36 ++++---- 15 files changed, 216 insertions(+), 228 deletions(-) diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 05a8d837a42..92d90df037f 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -27,15 +27,15 @@ integer reference. .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst :language: pulse - :start-after: ```pulse //incr - :end-before: ``` + :start-after: //incr + :end-before: //end incr And here's a function to increment two references in parallel. .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst :language: pulse - :start-after: ```pulse //par_incr - :end-before: ``` + :start-after: //par_incr + :end-before: //end par_incr You may not have heard about separation logic before---but perhaps these specifications already make intuitive sense to you. The type of diff --git a/book/pulse/pulse_arrays.rst b/book/pulse/pulse_arrays.rst index d49b6a3a638..1880d7e948d 100644 --- a/book/pulse/pulse_arrays.rst +++ b/book/pulse/pulse_arrays.rst @@ -24,8 +24,8 @@ that reads an array: .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst :language: pulse - :start-after: ```pulse //readi$ - :end-before: ``` + :start-after: //readi$ + :end-before: //end readi$ The library provides a points-to predicate ``pts_to arr #p s`` with the interpretation that in the current memory, the contents of ``arr`` @@ -50,8 +50,8 @@ As another example, let's write to the ``i``-th element of an array: .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst :language: pulse - :start-after: ```pulse //writei$ - :end-before: ``` + :start-after: //writei$ + :end-before: //end writei$ The function uses the array write operator ``arr(i) <- x`` and the postcondition asserts that in the state when the function returns, the contents of the array @@ -121,7 +121,7 @@ of the array ``a2`` to ``a1``. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst :language: pulse :start-after: //copy$ - :end-before: ``` + :end-before: //end copy$ The loop invariant existentially abstracts over the contents of ``a1``, and maintains that up to the current loop counter, the contents of the two arrays are equal. Rest of @@ -162,8 +162,8 @@ above. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst :language: pulse - :start-after: ```pulse //compare_stack_arrays$ - :end-before: ``` + :start-after: //compare_stack_arrays$ + :end-before: //end compare_stack_arrays$ As with the stack references, stack arrays don't need to be deallocated or dropped, they are reclaimed automatically when the function returns. As a result, @@ -201,8 +201,8 @@ using the ``copy2`` function we wrote above. .. literalinclude:: ../code/pulse/PulseTutorial.Array.fst :language: pulse - :start-after: ```pulse //copyuse$ - :end-before: ``` + :start-after: //copyuse$ + :end-before: //end copyuse$ Note how the assertion for ``v`` transforms from ``V.pts_to`` to ``pts_to`` (the points-to assertion for arrays) and back. It means that array algorithms and routines can be implemented with the diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst index ff5377324e1..b4afbc599f6 100755 --- a/book/pulse/pulse_atomics_and_invariants.rst +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -167,7 +167,7 @@ full-permission on ``x``. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: fstar :start-after: //owns$ - :end-before: //owns$ + :end-before: //end owns$ Notice the type annotation on ``owns`` claims that it is ``boxable``, and indeed F*'s refinement type checker automatically proves that it @@ -179,7 +179,7 @@ an invariant ``inv i (owns r)``, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: pulse :start-after: //create_invariant$ - :end-before: ``` + :end-before: //end create_invariant$ Importantly, when we turn ``pts_to r x`` into ``inv i (owns r)``, **we lose** ownership of ``pts_to r x``. Remember, once we have ``inv i @@ -206,7 +206,7 @@ typechecking fails, as shown in the example below: .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: pulse :start-after: //create_non_boxable$ - :end-before: ``` + :end-before: //end create_non_boxable$ failing with an error pointing to the source location of the refinement precondition, ``is_big``, at the call to ``new_invariant``. @@ -306,7 +306,7 @@ update a reference with a pre- and postcondition of ``emp``. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: pulse :start-after: //update_ref_atomic$ - :end-before: ``` + :end-before: //end update_ref_atomic$ * At the start of the ``with_invariants`` scope, we have ``owns r`` in the context. @@ -337,7 +337,7 @@ within the same scope, then it's easy to prove ``False``: .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: pulse :start-after: //double_open_bad$ - :end-before: //double_open_bad$ + :end-before: //end double_open_bad$ Here, we open the invariants ``i`` twice and get ``owns r ** owns r``, or more than full permission to ``r``---from this, it is easy to build @@ -355,7 +355,7 @@ doesn't record which invariants were opened internally. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: pulse :start-after: //update_ref$ - :end-before: ``` + :end-before: //end update_ref$ This is okay, since a non-atomic computation can never appear within a ``with_invariants`` block---so, there's no fear of an ``stt`` @@ -367,9 +367,9 @@ error, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: pulse :start-after: //update_ref_fail$ - :end-before: //update_ref_fail$ + :end-before: //end update_ref_fail$ .. code-block:: - This computation is not atomic nor ghost. `with_invariants` - blocks can only contain atomic computations. + blocks can only contain atomic computations. \ No newline at end of file diff --git a/book/pulse/pulse_ch1.rst b/book/pulse/pulse_ch1.rst index 2c5bc9a002f..c91a4aa0fd1 100644 --- a/book/pulse/pulse_ch1.rst +++ b/book/pulse/pulse_ch1.rst @@ -3,9 +3,9 @@ Pulse Basics ============ -A Pulse program is embedded in an F* program, where the Pulse parts -are delimited by a syntax extension marker "\`\`\`pulse ... \`\`\`", as -shown in the program below. +A Pulse program is embedded in an F* program by including the +directive ``#lang-pulse`` in an F* file. The rest of the file can +then use a mixture of Pulse and F* syntax, as shown below. .. literalinclude:: ../code/pulse/PulseByExample.fst :language: pulse @@ -19,11 +19,9 @@ definition and proves that it always returns the constant defined in Pulse. This is a really simple program, but it already illustrates how Pulse and F* interact in both directions. -In what follows, unless we really want to emphasize that a fragment of -code is Pulse embedded in a larger F* context, when showing Pulse -code, we'll just show the Pulse part, omitting the "\`\`\`pulse -... \`\`\`" delimiters. - +In what follows, unless we really want to emphasize that a fragment of code is +Pulse embedded in a larger F* context, we just assume that we're working in a +context where ``#lang-pulse`` is enabled. A Separation Logic Primer ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,8 +152,8 @@ requires permission only to ``x`` and increments it: .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst :language: pulse - :start-after: ```pulse //incr - :end-before: ``` + :start-after: //incr$ + :end-before: //end incr$ Because of the frame rule, we can also call ``incr`` in a context like ``incr_frame`` below, and we can prove without any additional work @@ -163,16 +161,16 @@ that ``y`` is unchanged. .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst :language: pulse - :start-after: ```pulse //incr_frame - :end-before: ``` + :start-after: //incr_frame$ + :end-before: //end incr_frame$ In fact, Pulse lets us use the frame rule with any ``f:vprop``, and we get, for free, that ``incr x`` does not disturb ``f``. .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst :language: pulse - :start-after: ```pulse //incr_frame_any - :end-before: ``` + :start-after: //incr_frame_any$ + :end-before: //end incr_frame_any$ A point about the notation: The variable ``'i`` is an implicitly bound logical variable, representing the value held in the ref-cell ``x`` in @@ -183,8 +181,8 @@ this is equivalent: .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst :language: pulse - :start-after: ```pulse //incr_explicit_i - :end-before: ``` + :start-after: //incr_explicit_i$ + :end-before: //end incr_explicit_i$ **Other vprop connectives** In addition the separating conjunction, Pulse, like other separation logics, provides other ways to combine diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index 504ae997eac..89e74cf9299 100644 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -36,20 +36,19 @@ function to swap the contents of two references: .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //swap$ - :end-before: ``` + :start-after: //swap$ + :end-before: //end swap$ Reading a reference ................... - Let's start by taking a closer look at how dereferencing works in the function ``value_of`` below: .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //value_of$ - :end-before: ``` + :start-after: //value_of$ + :end-before: //end value_of$ Its slightly more explicit form is shown below, where ``w:erased a`` is an erased value witnessing the current contents referenced by @@ -57,8 +56,8 @@ is an erased value witnessing the current contents referenced by .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //value_of_explicit$ - :end-before: ``` + :start-after: //value_of_explicit$ + :end-before: //end value_of_explicit$ Notice how the precondition requires ``pts_to r w`` while the postcondition retains ``pts_to r w``, along with the property that ``v @@ -76,8 +75,8 @@ code fails to check with the error shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //value_of_explicit_fail$ - :end-before: ``` + :start-after: //value_of_explicit_fail$ + :end-before: //end value_of_explicit_fail$ .. code-block:: @@ -92,8 +91,8 @@ reference---the specification shows that when the function returns, .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //assign$ - :end-before: ``` + :start-after: //assign$ + :end-before: //end assign$ Dereferencing is explicit @@ -101,7 +100,7 @@ Dereferencing is explicit Unlike languages like C or Rust which make a distinction between l-values and r-values and implicitly read the content of references, -in Pulse (like in OCaml), references are explicitly dereferenced. [#]_ +in Pulse (like in OCaml), references are explicitly dereferenced. As the program below illustrates, references themselves can be passed to other functions (e.g., as in/out-parameters) while their current values must be passed explicitly. @@ -111,16 +110,16 @@ The function ``add`` takes both a reference ``r:ref int`` and a value .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //add$ - :end-before: ``` + :start-after: //add$ + :end-before: //end add$ Meanwhile, the function ``quadruple`` calls ``add`` twice to double the value stored in ``r`` each time. .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //quadruple$ - :end-before: ``` + :start-after: //quadruple$ + :end-before: //end quadruple$ Inspecting the proof state .......................... @@ -159,8 +158,8 @@ middle. .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //quadruple_show_proof_state$ - :end-before: ``` + :start-after: //quadruple_show_proof_state$ + :end-before: //end quadruple_show_proof_state$ The output from ``show_proof_state`` is shown below: @@ -198,7 +197,6 @@ The comments show how the proof state evolves after each command. * Pulse also uses the SMT solver to convert ``pts_to r (v2 + v2)`` to ``pts_to r (4 * 'v)``. - Stateful commands are explicitly sequenced .......................................... @@ -208,8 +206,8 @@ Pulse expects the results of all stateful operations to be explicitly .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //quad FAIL$ - :end-before: ``` + :start-after: //quad FAIL$ + :end-before: //end quad FAIL$ .. code-block:: @@ -265,16 +263,16 @@ previously, we can write down the permissions explicitly. .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //assign_1.0R$ - :end-before: ``` + :start-after: //assign_1.0R$ + :end-before: //end assign_1.0R$ In contrast, when reading a reference, any permission ``p`` will do, as shown below: .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //value_of_perm$ - :end-before: ``` + :start-after: //value_of_perm$ + :end-before: //end value_of_perm$ If we try to write to a reference without holding full permission on it, Pulse rejects the program, as shown below. @@ -296,16 +294,15 @@ The full error message requires the F* option ``--print_implicits``. The functions ``share`` and ``gather`` allow one to divide and combine permissions on references, as shown below. - .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //share_ref$ - :end-before: ``` + :start-after: //share_ref$ + :end-before: //end share_ref$ .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //gather_ref$ - :end-before: ``` + :start-after: //gather_ref$ + :end-before: //end gather_ref$ The type of ``gather_ref`` has an additional interesting element: its postcondition proves that ``'v0 == 'v1``. That is, since ``x`` can @@ -313,11 +310,9 @@ point to at most one value, given two separate points-to assertions about ``x``, allows one to conclude that the pointed-to witnesses are identical. - Stack references ^^^^^^^^^^^^^^^^ - ``let mut`` creates a new stack ref ................................... @@ -326,8 +321,8 @@ Pulse, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //one - :end-before: ``` + :start-after: //one + :end-before: //end one The body of the program is annotated to show program assertions that are true after each command. @@ -351,7 +346,7 @@ are true after each command. memory it points to is reclaimed, and the ``pts_to x #1.0R _v`` is consumed. - + A few additional points to note here: @@ -373,8 +368,8 @@ scoped, let's look at the program below that Pulse refuses to check: .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //refs_as_scoped FAIL - :end-before: ``` + :start-after: //refs_as_scoped FAIL + :end-before: //end refs_as_scoped FAIL The error points to the location of ``s`` with the message below, meaning that the current assertion on the heap is only ``emp``, while @@ -389,7 +384,7 @@ scope. - In the context: emp - + Heap references ^^^^^^^^^^^^^^^ @@ -409,8 +404,8 @@ as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Box.fst :language: pulse - :start-after: ```pulse //new_heap_ref$ - :end-before: ``` + :start-after: //new_heap_ref$ + :end-before: //end new_heap_ref$ Note, we can return a freshly allocated heap reference from a function, unlike a ``let mut`` scoped, stack-allocated reference. @@ -420,8 +415,8 @@ In the following example, we use ``open Box;`` to open the namespace .. literalinclude:: ../code/pulse/PulseTutorial.Box.fst :language: pulse - :start-after: ```pulse //last_value_of$ - :end-before: ``` + :start-after: //last_value_of$ + :end-before: //end last_value_of$ ``box t`` references can be demoted to regular ``ref t`` references for code reuse. For example, in the code below, we increment the @@ -432,8 +427,8 @@ then calling ``incr (box_to_ref r)``; and then converting back to a .. literalinclude:: ../code/pulse/PulseTutorial.Box.fst :language: pulse - :start-after: ```pulse //incr_box$ - :end-before: ``` + :start-after: //incr_box$ + :end-before: //end incr_box$ Finally, unlike Rust's ``Box`` type, which is always treated linearly (i.e., in Rust, one always holds exclusive read/write @@ -442,8 +437,3 @@ implicit fractional permission as with regular references. Ghost references ^^^^^^^^^^^^^^^^ - - -.. [#] We are considering switching this convention and the Pulse - option ``--ext 'pulse:rvalues'`` can be enabled to add implicit - dereferencing; however, it is not yet recommended for use. diff --git a/book/pulse/pulse_conditionals.rst b/book/pulse/pulse_conditionals.rst index 5b25d658089..5852b7e7870 100644 --- a/book/pulse/pulse_conditionals.rst +++ b/book/pulse/pulse_conditionals.rst @@ -16,8 +16,8 @@ references. .. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse - :start-after: //SNIPPET_START: max$ - :end-before: //SNIPPET_END: max$ + :start-after: //max$ + :end-before: //end max$ This program illustrates a very common specification style. @@ -42,8 +42,8 @@ fails, with the error message shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse - :start-after: ```pulse //max_alt_fail$ - :end-before: ``` + :start-after: //max_alt_fail$ + :end-before: //end max_alt_fail$ .. code-block:: @@ -54,8 +54,8 @@ Here's an annotated version of ``max_alt`` that succeeds. .. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse - :start-after: ```pulse //max_alt$ - :end-before: ``` + :start-after: //max_alt$ + :end-before: //end max_alt$ We are working on adding inference for non-tail conditionals. @@ -67,8 +67,8 @@ representation of a possibly null reference. .. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: fstar - :start-after: //SNIPPET_START: nullable_ref$ - :end-before: //SNIPPET_END: nullable_ref$ + :start-after: //nullable_ref$ + :end-before: //end nullable_ref$ Representation predicate ++++++++++++++++++++++++ @@ -130,8 +130,8 @@ Let's try our first pattern match in Pulse: .. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse - :start-after: ```pulse //read_nullable$ - :end-before: ``` + :start-after: //read_nullable$ + :end-before: //end read_nullable$ The syntax of pattern matching in Pulse is more imperative and Rust-like than what F* uses. @@ -175,8 +175,8 @@ condition hypothesis for the preceding branches not taken. .. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse - :start-after: //SNIPPET_START: read_nullable_alt_fail$ - :end-before: //SNIPPET_END: read_nullable_alt_fail$ + :start-after: //read_nullable_alt_fail$ + :end-before: //end read_nullable_alt_fail$ We plan to enhance the Pulse checker to also provide these negated path conditions. @@ -194,8 +194,8 @@ every time, one can define helper functions to handle these cases. .. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse - :start-after: //SNIPPET_START: pts_to_or_null_helpers$ - :end-before: //SNIPPET_END: pts_to_or_null_helpers$ + :start-after: //pts_to_or_null_helpers$ + :end-before: //end pts_to_or_null_helpers$ These functions are all marked ``ghost``, indicating that they are purely for proof purposes only. @@ -211,8 +211,8 @@ easier: .. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse - :start-after: ```pulse //read_nullable_alt$ - :end-before: ``` + :start-after: //read_nullable_alt$ + :end-before: //end read_nullable_alt$ Writing a nullable reference @@ -223,8 +223,8 @@ here is a function to write a nullable reference. .. literalinclude:: ../code/pulse/PulseTutorial.Conditionals.fst :language: pulse - :start-after: ```pulse //write_nullable$ - :end-before: ``` + :start-after: //write_nullable$ + :end-before: //end write_nullable$ diff --git a/book/pulse/pulse_existentials.rst b/book/pulse/pulse_existentials.rst index 6f3b1fa9dca..3b731929578 100644 --- a/book/pulse/pulse_existentials.rst +++ b/book/pulse/pulse_existentials.rst @@ -35,16 +35,16 @@ never actually used in any other predicate. .. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst :language: pulse - :start-after: ```pulse //assign$ - :end-before: ``` + :start-after: //assign$ + :end-before: //end assign$ And indeed, another way to write the specification of ``assign``, without the logical variable argument, is shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst :language: pulse - :start-after: ```pulse //assign$ - :end-before: ``` + :start-after: //assign$ + :end-before: //end assign$ This time, in the precondition, we use an existential quantifier to say that ``assign`` is callable in a context where ``x`` points to any @@ -62,8 +62,8 @@ postcondition. .. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst :language: pulse - :start-after: //SNIPPET_START: incr_fail$ - :end-before: //SNIPPET_END: incr_fail$ + :start-after: //incr_fail$ + :end-before: //end incr_fail$ However, existential quantification often appears in postconditions, e.g., in order to abstract the behavior of function by underspecifying @@ -75,8 +75,8 @@ depend on the initial value of ``x``. .. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst :language: pulse - :start-after: ```pulse //make_even$ - :end-before: ``` + :start-after: //make_even$ + :end-before: //end make_even$ Manipulating existentials ......................... @@ -92,8 +92,8 @@ useful, and we show a first example of how they work below .. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst :language: pulse - :start-after: ```pulse //make_even_explicit$ - :end-before: ``` + :start-after: //make_even_explicit$ + :end-before: //end make_even_explicit$ Eliminating existentials ++++++++++++++++++++++++ @@ -123,8 +123,8 @@ binders. .. literalinclude:: ../code/pulse/PulseTutorial.Existentials.fst :language: pulse - :start-after: ```pulse //make_even_explicit_alt$ - :end-before: ``` + :start-after: //make_even_explicit_alt$ + :end-before: //end make_even_explicit_alt$ When there is a single existential formula in the context, one can write ``with x1..xn. _`` to "open" the formula, binding its witnesses diff --git a/book/pulse/pulse_ghost.rst b/book/pulse/pulse_ghost.rst index fb8d6e82a0e..5f53dc28f77 100755 --- a/book/pulse/pulse_ghost.rst +++ b/book/pulse/pulse_ghost.rst @@ -35,7 +35,7 @@ below. .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //incr_erased_non_ghost$ - :end-before: //incr_erased_non_ghost$ + :end-before: //end incr_erased_non_ghost$ .. code-block:: @@ -53,7 +53,7 @@ works: .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //incr_erased$ - :end-before: ``` + :end-before: //end incr_erased$ The ``ghost`` qualifier indicates the the Pulse checker that the function is to be erased at runtime, so ``ghost`` functions are @@ -67,7 +67,7 @@ fails with the error below: .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //try_use_incr_erased$ - :end-before: //try_use_incr_erased$ + :end-before: //end try_use_incr_erased$ .. code-block:: @@ -89,7 +89,7 @@ function to wrap the call to ``incr_erased``. .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //use_incr_erased$ - :end-before: //use_incr_erased$ + :end-before: //end use_incr_erased$ The library also contains ``Pulse.Lib.Pervasives.call_ghost`` that is a higher-order combinator to erase the result of a ghost call. @@ -97,7 +97,7 @@ a higher-order combinator to erase the result of a ghost call. .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //use_incr_erased_alt$ - :end-before: ``` + :end-before: //end use_incr_erased_alt$ The ``call_ghost`` combinator can be used with ghost functions of different arities, though it requires the applications to be curried @@ -108,14 +108,14 @@ Suppose we have a binary ghost function, like ``add_erased``: .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //add_erased$ - :end-before: ``` + :end-before: //end add_erased$ To call it in a non-ghost context, one can do the following: .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //use_add_erased$ - :end-before: ``` + :end-before: //end use_add_erased$ That said, since ``ghost`` functions must have non-informative return types to be usable in non-ghost contexts, it's usually best to define @@ -125,7 +125,7 @@ call site, as shown below: .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //add_erased_erased$ - :end-before: ``` + :end-before: //end add_erased_erased$ Some Primitive Ghost Functions @@ -148,7 +148,7 @@ library. Its signature looks like this: .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //__rewrite_sig$ - :end-before: //__rewrite_sig$ + :end-before: //end __rewrite_sig$ Many of the other primitives like ``fold``, ``unfold``, etc. are defined in terms of ``rewrite`` and are ``ghost`` computations. @@ -160,7 +160,7 @@ below: .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //intro_exists_sig$ - :end-before: //intro_exists_sig$ + :end-before: //end intro_exists_sig$ .. _Pulse_recursive_predicates: @@ -184,7 +184,7 @@ contain integers whose value is at most ``n``. The recursive predicate .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: fstar :start-after: //all_at_most$ - :end-before: //all_at_most$ + :end-before: //end all_at_most$ As we did when working with :ref:`nullable references `, it's useful to define a few helper @@ -206,7 +206,7 @@ m``. .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //weaken_at_most$ - :end-before: ``` + :end-before: //end weaken_at_most$ A few points to note: @@ -296,7 +296,7 @@ to hold the same value. .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //correlated$ - :end-before: //correlated$ + :end-before: //end correlated$ Now, here's the signature of a function ``use_temp``: at first glance, from its signature alone, one might think that the witness ``v0`` @@ -306,7 +306,7 @@ postcondition. .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //use_temp_sig$ - :end-before: //use_temp_sig$ + :end-before: //end use_temp_sig$ But, ``use_temp`` only has half-permission to the ghost reference and cannot mutate it. So, although it can mutate the reference itself, in @@ -316,7 +316,7 @@ initial value. .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //use_temp_body$ - :end-before: //use_temp_body$ + :end-before: //end use_temp_body$ This property can be exploited by a caller to pass a reference to ``use_temp`` and be assured that the value is unchanged when it @@ -325,5 +325,5 @@ returns. .. literalinclude:: ../code/pulse/PulseTutorial.Ghost.fst :language: pulse :start-after: //use_correlated$ - :end-before: ``` + :end-before: //end use_correlated$ diff --git a/book/pulse/pulse_higher_order.rst b/book/pulse/pulse_higher_order.rst index e0234dbde2b..3654e09d604 100755 --- a/book/pulse/pulse_higher_order.rst +++ b/book/pulse/pulse_higher_order.rst @@ -16,7 +16,7 @@ function application. .. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst :language: pulse :start-after: //apply$ - :end-before: ``` + :end-before: //end apply$ This function is polymorphic in the argument, result type, pre, and post-condition of a function ``f``, which it applies to an argument @@ -50,7 +50,7 @@ functions. .. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst :language: pulse :start-after: //apply_ghost$ - :end-before: ``` + :end-before: //end apply_ghost$ The type of ``f`` is similar to what we had before, but this time we have: @@ -105,7 +105,7 @@ type ``ctr`` of a counter. .. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst :language: fstar :start-after: //ctr$ - :end-before: //ctr$ + :end-before: //end ctr$ A counter packages the following: @@ -125,7 +125,7 @@ heap-allocated reference. This is what ``new_counter`` does below: .. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst :language: pulse :start-after: //new_counter$ - :end-before: ``` + :end-before: //end new_counter$ Here's how it works. @@ -147,7 +147,7 @@ completely hidden. .. literalinclude:: ../code/pulse/PulseTutorial.HigherOrder.fst :language: pulse :start-after: //test_counter$ - :end-before: ``` + :end-before: //end test_counter$ diff --git a/book/pulse/pulse_implication_and_forall.rst b/book/pulse/pulse_implication_and_forall.rst index 2b4a1cadacc..a805193cf45 100755 --- a/book/pulse/pulse_implication_and_forall.rst +++ b/book/pulse/pulse_implication_and_forall.rst @@ -12,12 +12,12 @@ connectives. Trades, or Separating Ghost Implication ........................................ -The library ``module I = Pulse.Lib.Stick.Util`` defines the operator -*trade* ``(@==>)`` and utilities for using it. In the literature, the -operator ``p --* q`` is pronounced "p magic-wand q"; ``p @==> q`` is -similar, though there are some important technical differences, as -we'll see. We'll just pronounce it ``p for q``, or ``p trades for q``. -Here's an informal description of what ``p @==> q`` means: +The library ``module I = Pulse.Lib.Trade.Util`` defines the operator *trade* +``(@==>)`` and utilities for using it. In the literature, the operator ``p --* +q`` is pronounced "p magic-wand q"; ``p @==> q`` is similar, though there are +some important technical differences, as we'll see. We'll just pronounce it ``p +for q``, ``p trade q``, or ``p trades for q``. Here's an informal description of +what ``p @==> q`` means: ``p @==> q`` says that if you have ``p`` then you can *trade* it for ``q``. In other words, from ``p ** (p @==> q)``, you can derive @@ -61,7 +61,7 @@ Here's a small example to see ``p @==> q`` at work. .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: fstar :start-after: //regain_half$ - :end-before: //regain_half$ + :end-before: //end regain_half$ The predicate ``regain_half`` says that you can trade a half-permission ``pts_to x #one_half v`` for a full permission @@ -77,7 +77,7 @@ Let's look at how to introduce ``regain_half``: .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: pulse :start-after: //intro_regain_half$ - :end-before: ``` + :end-before: //end intro_regain_half$ The specification says that if we start out with ``pts_to x 'v`` then we can split it into ``pts_to x #one_half v`` and a ``regain_half x @@ -100,7 +100,7 @@ permission, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: pulse :start-after: //use_regain_half$ - :end-before: ``` + :end-before: //end use_regain_half$ At this point, you may be wondering why we bother to use a ``regain_half x 'v`` in the first place, since one might as well have @@ -116,7 +116,7 @@ Let's look at our ``regain_half`` predicate again: .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: fstar :start-after: //regain_half$ - :end-before: //regain_half$ + :end-before: //end regain_half$ This predicate is not as general as it could be: to eliminate it, it requires the caller to prove that they holds ``pts_to x #one_half v``, @@ -138,7 +138,7 @@ Here's the right version of ``regain_half``: .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: fstar :start-after: //regain_half_q$ - :end-before: //regain_half_q$ + :end-before: //end regain_half_q$ This says that no matter what ``pts_to x #one_half u`` the context has, they can recover full permission to it, *with the same witness* @@ -202,7 +202,7 @@ Here's how one introduces ``regain_half_q``: .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: pulse :start-after: //intro_regain_half_q$ - :end-before: ``` + :end-before: //end intro_regain_half_q$ Now, when we want to use it, we can trade in any half-permission on ``pts_to x #one_half u``, for a full permission with the same ``u``. @@ -210,7 +210,7 @@ Now, when we want to use it, we can trade in any half-permission on .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: pulse :start-after: //use_regain_half_q$ - :end-before: ``` + :end-before: //end use_regain_half_q$ Note using the eliminator for ``FA.elim`` is quite verbose: we need to specify the quantifier term again. The way Pulse uses F*'s unifier @@ -233,7 +233,7 @@ permission to ``pts_to x #one_half u`` for a full permission to a .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: fstar :start-after: //can_update$ - :end-before: //can_update$ + :end-before: //end can_update$ In ``make_can_update``, we package a ghost-state update function into a binary quantifier ``forall* u v. ...``. @@ -241,7 +241,7 @@ a binary quantifier ``forall* u v. ...``. .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: pulse :start-after: //make_can_update$ - :end-before: ``` + :end-before: //end make_can_update$ And in ``update``, below, we instantiate it to update the reference ``x`` from ``'u`` to ``k``, and also return back a ``can_update`` @@ -250,5 +250,5 @@ predicate to the caller, for further use. .. literalinclude:: ../code/pulse/PulseTutorial.ImplicationAndForall.fst :language: pulse :start-after: //update$ - :end-before: ``` + :end-before: //end update$ diff --git a/book/pulse/pulse_linked_list.rst b/book/pulse/pulse_linked_list.rst index 485f6a7dbfa..a15347d6494 100755 --- a/book/pulse/pulse_linked_list.rst +++ b/book/pulse/pulse_linked_list.rst @@ -16,7 +16,7 @@ Let's start by defining the type of a singly linked list: .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: fstar :start-after: //llist$ - :end-before: //llist$ + :end-before: //end llist$ A ``node t`` contains a ``head:t`` and a ``tail:llist t``, a nullable reference pointing to the rest of the list. Nullable references are @@ -29,7 +29,7 @@ representation of the list, for use in specifications. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: fstar :start-after: //is_list$ - :end-before: //is_list$ + :end-before: //end is_list$ The predicate ``is_list x l`` is a recursive predicate: @@ -52,7 +52,7 @@ but, for now, we are forced to write them by hand. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //boilerplate$ - :end-before: //boilerplate$ + :end-before: //end boilerplate$ @@ -70,14 +70,14 @@ based on whether or not ``x`` is null. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: fstar :start-after: //is_list_cases$ - :end-before: //is_list_cases$ + :end-before: //end is_list_cases$ Next, we define a ghost function to invert ``is_list`` into ``is_list_cases``. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //cases_of_is_list$ - :end-before: ``` + :end-before: //end cases_of_is_list$ We also define two more ghost functions that package up the call to ``cases_of_is_list``. @@ -85,12 +85,12 @@ We also define two more ghost functions that package up the call to .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //is_list_case_none$ - :end-before: ``` + :end-before: //end is_list_case_none$ .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //is_list_case_some$ - :end-before: ``` + :end-before: //end is_list_case_some$ Length, Recursively ................... @@ -101,7 +101,7 @@ code, starting with a function to compute the length of an ``llist``. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //length$ - :end-before: ``` + :end-before: //end length$ The ``None`` case is simple. @@ -150,7 +150,7 @@ cell starting at the given node. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //tail_for_cons$ - :end-before: ``` + :end-before: //end tail_for_cons$ Tail of a list @@ -194,7 +194,7 @@ can get back the original permission on ``x``. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //tail$ - :end-before: ``` + :end-before: //end tail$ ``length_iter`` +++++++++++++++ @@ -208,7 +208,7 @@ the tail and increment ``ctr``. Finally, we return the ``!ctr``. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //length_iter$ - :end-before: ``` + :end-before: //end length_iter$ Now, for the proof. The main part is the loop invariant, which says: @@ -255,7 +255,7 @@ to point to ``y``. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //append$ - :end-before: ``` + :end-before: //end append$ The code is tail recursive in the ``Some _`` case, but notice that we have a ghost function call *after* the recursive call. Like we did for @@ -275,7 +275,7 @@ recovered by instantiating ``tl'`` to ``tl``. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //tail_alt$ - :end-before: ``` + :end-before: //end tail_alt$ We'll use these quantified trades in our invariant of ``append_iter``, shown below. The main idea of the implementation is to use a while @@ -285,7 +285,7 @@ to set ``y`` as the ``next`` pointer of this last element. .. literalinclude:: ../code/pulse/PulseTutorial.LinkedList.fst :language: pulse :start-after: //append_iter$ - :end-before: ``` + :end-before: //end append_iter$ There are few interesting points to note. diff --git a/book/pulse/pulse_loops.rst b/book/pulse/pulse_loops.rst index 1bc3302bae8..f8f975eb5be 100755 --- a/book/pulse/pulse_loops.rst +++ b/book/pulse/pulse_loops.rst @@ -71,8 +71,8 @@ decrements a reference until it reaches ``0``. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: pulse - :start-after: ```pulse //count_down$ - :end-before: ``` + :start-after: //count_down$ + :end-before: //end count_down$ While loops in Pulse are perhaps a bit more general than in other languages. The ``guard`` is an arbitrary Pulse program, not just a @@ -83,8 +83,8 @@ variable. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: pulse - :start-after: ```pulse //count_down2$ - :end-before: ``` + :start-after: //count_down3$ + :end-before: //end count_down3$ Partial correctness @@ -95,8 +95,8 @@ infinitely looping variant of our program is also accepted: .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: pulse - :start-after: ```pulse //count_down_loopy$ - :end-before: ``` + :start-after: //count_down_loopy$ + :end-before: //end count_down_loopy$ This program increments instead of decrement ``x``, but it still satisfies the same invariant as before, since it always loops forever. @@ -121,8 +121,8 @@ implements multiplication by repeated addition. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: pulse - :start-after: ```pulse //multiply_by_repeated_addition$ - :end-before: ``` + :start-after: //multiply_by_repeated_addition$ + :end-before: //end multiply_by_repeated_addition$ A few noteworthy points: @@ -149,8 +149,8 @@ this sum. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: fstar - :start-after: //SNIPPET_START: sum$ - :end-before: //SNIPPET_END: sum$ + :start-after: //sum$ + :end-before: //end sum$ Now, let's say we want to implement ``isum``, an iterative version of ``sum``, and prove that it satisfies the identity proven by @@ -158,8 +158,8 @@ Now, let's say we want to implement ``isum``, an iterative version of .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: pulse - :start-after: //SNIPPET_START: isum$ - :end-before: //SNIPPET_END: isum$ + :start-after: //isum$ + :end-before: //end isum$ This program is quite similar to ``multiply_by_repeated_addition``, but with a couple of differences: @@ -192,7 +192,7 @@ computes the nth Fibonacci number. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: fstar :start-after: //fib$ - :end-before: //fib$ + :end-before: //end fib$ One can also implement it in Pulse, as ``fib_rec`` while using an out-parameter to hold that values of the last two Fibonacci numbers in @@ -201,7 +201,7 @@ the sequence. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: pulse :start-after: //fib_rec$ - :end-before: ``` + :end-before: //end fib_rec$ Some points to note here: @@ -220,7 +220,7 @@ loop, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.Loops.fst :language: pulse :start-after: //fib_loop$ - :end-before: ``` + :end-before: //end fib_loop$ diff --git a/book/pulse/pulse_parallel_increment.rst b/book/pulse/pulse_parallel_increment.rst index d5e39a58b28..005d9df6398 100755 --- a/book/pulse/pulse_parallel_increment.rst +++ b/book/pulse/pulse_parallel_increment.rst @@ -59,15 +59,15 @@ below: .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse :start-after: //par$ - :end-before: ``` + :end-before: //end par$ As we saw in the :ref:`introduction to Pulse `, it's easy to increment two separate references in parallel: .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst :language: pulse - :start-after: ```pulse //par_incr - :end-before: ``` + :start-after: //par_incr$ + :end-before: //end par_incr$ But, what if we wanted to increment the same reference in two separate threads? That is, we wanted to program something like this: @@ -106,8 +106,8 @@ To enforce synchronization, we could use a lock, e.g., shown below: .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse - :start-after: ```pulse //attempt$ - :end-before: ``` + :start-after: //attempt$ + :end-before: //end attempt$ This program is type correct and free from data races. But, since the lock holds the entire permission on ``x``, there's no way to give this @@ -143,7 +143,7 @@ protected by the lock: .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: fstar :start-after: //lock_inv$ - :end-before: //lock_inv$ + :end-before: //end lock_inv$ Our strawman ``lock`` in the ``attempt`` shown before had type ``lock (exists* v. pts_to x v)``. This time, we add a conjunct that refines @@ -165,8 +165,8 @@ Here's the code for the left thread, ``incr_left``: .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse - :start-after: ```pulse //incr_left$ - :end-before: ``` + :start-after: //incr_left$ + :end-before: //end incr_left$ * Its arguments include ``x`` and the ``lock``, but also both pieces of ghost state, ``left`` and ``right``, and an erased value ``i`` @@ -205,15 +205,15 @@ this shortly. .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse - :start-after: ```pulse //incr_right$ - :end-before: ``` + :start-after: //incr_right$ + :end-before: //end incr_right$ Finally, we can implement ``add2`` with the specification we want: .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse - :start-after: ```pulse //add2$ - :end-before: ``` + :start-after: //add2$ + :end-before: //end add2$ * We allocate ``left`` and ``right`` ghost references, initializing them to ``0``. @@ -252,8 +252,8 @@ ghost state. Here's how: .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse - :start-after: ```pulse //incr$ - :end-before: ``` + :start-after: //incr$ + :end-before: //end incr$ As before, ``incr`` requires ``x`` and the ``lock``, but, this time, it is parameterized by: @@ -284,8 +284,8 @@ as ``add2``. .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse - :start-after: ```pulse //add2_v2$ - :end-before: ``` + :start-after: //add2_v2$ + :end-before: //end add2_v2$ The code is just a rearrangement of what we had before, factoring the ghost code in ``incr_left`` and ``incr_right`` into a ghost function @@ -325,7 +325,7 @@ problem of adapting this to ``U32.t``. .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: fstar :start-after: //atomic_primitives$ - :end-before: //atomic_primitives$ + :end-before: //end atomic_primitives$ Cancellable Invariants ++++++++++++++++++++++ @@ -409,7 +409,7 @@ Our first step is to build an increment operation from an .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse :start-after: //incr_atomic_spec$ - :end-before: //incr_atomic_body$ + :end-before: //end incr_atomic_spec$ The style of specification is similar to the generic style we used with ``incr``, except now we use cancellable invariant instead of a @@ -424,7 +424,7 @@ The ``read`` function is relatively easy: .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse :start-after: //incr_atomic_body_read$ - :end-before: //incr_atomic_body_read$ + :end-before: //end incr_atomic_body_read$ * We open the invariant ``l``; then, knowing that the invariant is still active, we can unpack` it; then read the value ``v``; pack it @@ -435,7 +435,7 @@ The main loop of ``incr_atomic`` is next, shown below: .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse :start-after: //incr_atomic_body_loop$ - :end-before: //incr_atomic_body_loop$ + :end-before: //end incr_atomic_body_loop$ The loop invariant says: @@ -466,7 +466,7 @@ time using invariants, though it has the same specification as before. .. literalinclude:: ../code/pulse/PulseTutorial.ParallelIncrement.fst :language: pulse :start-after: //add2_v3$ - :end-before: ``` + :end-before: //end add2_v3$ The code too is very similar to ``add2_v2``, except instead of allocating a lock, we allocate a cancellable invariant. And, at the diff --git a/book/pulse/pulse_spin_lock.rst b/book/pulse/pulse_spin_lock.rst index f8b6c77fce9..8ae6a7bfec9 100755 --- a/book/pulse/pulse_spin_lock.rst +++ b/book/pulse/pulse_spin_lock.rst @@ -31,7 +31,7 @@ invariant: .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst :language: fstar :start-after: //lock$ - :end-before: //lock$ + :end-before: //end lock$ The predicate ``lock_inv r p`` states: @@ -54,7 +54,7 @@ allocated ``l:lock`` .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst :language: pulse :start-after: //new_lock$ - :end-before: ``` + :end-before: //end new_lock$ Importantly, since allocating a lock involves allocating an invariant that protects the predicate ``p``, we need ``p`` to be ``boxable``. @@ -79,7 +79,7 @@ duplicate ``lock_alive``. .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst :language: pulse :start-after: //dup_lock_alive$ - :end-before: ``` + :end-before: //end dup_lock_alive$ Acquiring a lock @@ -92,7 +92,7 @@ i.e., the precondition is ``emp``. .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst :language: pulse :start-after: //acquire_sig$ - :end-before: //acquire_sig$ + :end-before: //end acquire_sig$ This may be seem surprising at first. But, recall that we've stashed ``p`` inside the invariant stored in the lock, and ``acquire`` is @@ -114,7 +114,7 @@ Pulse.Lib.Reference: requires Box.pts_to r i returns b:bool ensures cond b (Box.pts_to r v ** pure (reveal i == u)) - (Box.pts_to r i)) + (Box.pts_to r i) The specification of ``cas_box r u v`` says that we can try to atomically @@ -127,7 +127,7 @@ function: .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst :language: pulse :start-after: //acquire_body$ - :end-before: ``` + :end-before: //end acquire_body$ The main part of the implementation is the ``with_invariants`` block. @@ -166,7 +166,7 @@ The signature is the dual of ``acquire``: the caller has to give up .. literalinclude:: ../code/pulse/PulseTutorial.SpinLock.fst :language: pulse :start-after: //release$ - :end-before: ``` + :end-before: //end release$ In this implementation, ``release`` unconditionally sets the reference to ``0ul`` and reproves the ``lock_inv``, since we have ``p`` in diff --git a/book/pulse/pulse_user_defined_predicates.rst b/book/pulse/pulse_user_defined_predicates.rst index 8c42cd77781..8d07add8264 100644 --- a/book/pulse/pulse_user_defined_predicates.rst +++ b/book/pulse/pulse_user_defined_predicates.rst @@ -18,8 +18,8 @@ A simple example of a user-defined abstraction is show below. .. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst :language: pulse - :start-after: //SNIPPET_START: pts_to_diag$ - :end-before: //SNIPPET_END: pts_to_diag$ + :start-after: //pts_to_diag$ + :end-before: //end pts_to_diag$ ``pts_to_diag r v`` is a ``vprop`` defined in F* representing a reference to a pair whose components are equal. @@ -29,8 +29,8 @@ explicit about folding and unfolding the predicate. .. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst :language: pulse - :start-after: ```pulse //double$ - :end-before: ``` + :start-after: //double$ + :end-before: //end double$ The ``unfold p`` command checks that ``p`` is provable in the current context by some term ``q``, and then rewrites the context by replacing @@ -57,8 +57,8 @@ provide all the arguments. .. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst :language: pulse - :start-after: ```pulse //double_alt$ - :end-before: ``` + :start-after: //double_alt$ + :end-before: //end double_alt$ Mutable Points .............. @@ -70,8 +70,8 @@ structure with two mutable integer fields, representing a .. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst :language: fstar - :start-after: //SNIPPET_START: point$ - :end-before: //SNIPPET_END: point$ + :start-after: //point$ + :end-before: //end point$ A ``point`` is just an F* record containing two references. Additionally, we define ``is_point``, a ``vprop``, @@ -84,8 +84,8 @@ offset ``dx, dy``. .. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst :language: pulse - :start-after: ```pulse //move$ - :end-before: ``` + :start-after: //move$ + :end-before: //end move$ Implementing ``move`` is straightforward, but like before, we have to ``unfold`` the ``is_point`` predicate first, and then fold it back up @@ -114,8 +114,8 @@ define a helper function to fold the predicate, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst :language: pulse - :start-after: ```pulse //fold_is_point$ - :end-before: ``` + :start-after: //fold_is_point$ + :end-before: //end fold_is_point$ .. note:: @@ -126,8 +126,8 @@ This allows type inference to work better, as shown below. .. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst :language: pulse - :start-after: ```pulse //move_alt$ - :end-before: ``` + :start-after: //move_alt$ + :end-before: //end move_alt$ .. _Pulse_rewriting: @@ -153,8 +153,8 @@ To illustrate this at work, consider the program below: .. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst :language: pulse - :start-after: ```pulse //create_and_move$ - :end-before: ``` + :start-after: //create_and_move$ + :end-before: //end create_and_move$ We allocate two references and put them in the structure ``p``. Now, to call ``fold_is_point``, we need ``pts_to p.x _`` and ``pts_to p.y @@ -213,8 +213,8 @@ Using ``rewrite each ...`` makes the code somewhat shorter: .. literalinclude:: ../code/pulse/PulseTutorial.UserDefinedPredicates.fst :language: pulse - :start-after: ```pulse //create_and_move_alt$ - :end-before: ``` + :start-after: //create_and_move_alt$ + :end-before: //end create_and_move_alt$ From 1074c377165942aa55d4f621412310083b0ee8f0 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 6 Apr 2025 15:40:11 -0700 Subject: [PATCH 53/65] s/vprop/slprop/g --- book/pulse/pulse_arrays.rst | 2 +- book/pulse/pulse_atomics_and_invariants.rst | 18 ++++---- book/pulse/pulse_ch1.rst | 47 ++++++++------------ book/pulse/pulse_ch2.rst | 12 ++--- book/pulse/pulse_conditionals.rst | 6 +-- book/pulse/pulse_existentials.rst | 6 +-- book/pulse/pulse_extraction.rst | 2 +- book/pulse/pulse_ghost.rst | 12 ++--- book/pulse/pulse_higher_order.rst | 4 +- book/pulse/pulse_implication_and_forall.rst | 16 +++---- book/pulse/pulse_linked_list.rst | 6 +-- book/pulse/pulse_parallel_increment.rst | 6 +-- book/pulse/pulse_spin_lock.rst | 2 +- book/pulse/pulse_user_defined_predicates.rst | 12 ++--- 14 files changed, 71 insertions(+), 80 deletions(-) diff --git a/book/pulse/pulse_arrays.rst b/book/pulse/pulse_arrays.rst index 1880d7e948d..e0557f429f2 100644 --- a/book/pulse/pulse_arrays.rst +++ b/book/pulse/pulse_arrays.rst @@ -42,7 +42,7 @@ Arrays can be read and written-to using indexes of type the index is within the array bounds---the refinement ``SZ.v i < Seq.length s`` enforces that the index is in bounds, where ``module SZ = FStar.SizeT``. The function returns the ``i``-th element of the -array, the asserted by the postcondition vprop ``pure (x == Seq.index +array, the asserted by the postcondition slprop ``pure (x == Seq.index s (SZ.v i))``. The body of the function uses the array read operator ``arr.(i)``. diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst index b4afbc599f6..381b9baaa51 100755 --- a/book/pulse/pulse_atomics_and_invariants.rst +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -73,7 +73,7 @@ computations described by the computation type below: .. code-block:: fstar - val stt_atomic (t:Type) (i:inames) (pre:vprop) (post:t -> vprop) + val stt_atomic (t:Type) (i:inames) (pre:slprop) (post:t -> slprop) : Type u#4 Like ``stt_ghost``, atomic computations are total and live in universe @@ -93,7 +93,7 @@ In ``Pulse.Lib.Core``, we have the following types: [@@erasable] val iref : Type0 - val inv (i:iref) (p:vprop) : vprop + val inv (i:iref) (p:slprop) : slprop Think of ``inv i p`` as a predicate asserting that ``p`` is true in the current state and all future states of the program. Every @@ -116,22 +116,22 @@ below: .. code-block:: fstar - val dup_inv (i:iref) (p:vprop) + val dup_inv (i:iref) (p:slprop) : stt_ghost unit emp_inames (inv i p) (fun _ -> inv i p ** inv i p) Boxable predicates ++++++++++++++++++ -Pulse's language of predicates, i.e., the type ``vprop``, is -stratified. The type ``boxable`` is a refinement of ``vprop``, defined +Pulse's language of predicates, i.e., the type ``slprop``, is +stratified. The type ``boxable`` is a refinement of ``slprop``, defined as shown below in ``Pulse.Lib.Core`` .. code-block:: fstar - let boxable = v:vprop { is_big v } + let boxable = v:slprop { is_big v } -That is, certain ``vprops``, i.e., those that satisfy ``is_big``, are +That is, certain ``slprops``, i.e., those that satisfy ``is_big``, are ``boxable`` predicates. All the predicates that we have encountered so far are boxable, except for the ``inv i p`` predicate. For example, ``pts_to x v`` is boxable; ``exists* x. p x`` is boxable if ``p x`` is @@ -223,7 +223,7 @@ an instance of ``inv i p`` is by provable that ``p`` is ``boxable``. This design is convenient since the onus of proving that a predicate is boxable is only placed at the allocation site of the invariant---uses of invariants do not need to worry about the -distinction between ``boxable`` and general ``vprops``. +distinction between ``boxable`` and general ``slprops``. Opening an invariant ++++++++++++++++++++ @@ -268,7 +268,7 @@ Here's the rule for opening a single invariant ``inv i p`` using ``with_invariant i { e }`` is as follows: * ``i`` must have type ``iref`` and ``inv i p`` must be provable in - the current context, for some ``p:vprop`` + the current context, for some ``p:slprop`` * ``e`` must have the type ``stt_atomic t j (p ** r) (fun x -> p ** s x)``. [#]_ That is, ``e`` requires and restores the invariant ``p``, diff --git a/book/pulse/pulse_ch1.rst b/book/pulse/pulse_ch1.rst index c91a4aa0fd1..c6d1aef922e 100644 --- a/book/pulse/pulse_ch1.rst +++ b/book/pulse/pulse_ch1.rst @@ -41,19 +41,18 @@ etc. Much like other Hoare Logics, which we reviewed in :ref:`an earlier section `, separation logic comes in two parts. -**Separation Logic Propositions** First, we have a language of -propositions that describe properties about program resources, e.g., -the heap. These propositions have the type ``vprop`` in Pulse, [#]_ -and, under the covers in the PulselCore semantics of Pulse, a ``vprop = -state -> prop``, where ``state`` represents the state of a program, -e.g., the contents of memory. It is useful (at least at first) to think -of a ``vprop`` as a memory property, though we will eventually treat -it more abstractly and use it to model many other kinds of resources. +**Separation Logic Propositions** First, we have a language of propositions that +describe properties about program resources, e.g., the heap. These propositions +have the type ``slprop`` in Pulse, and, under the covers in the PulselCore +semantics of Pulse, a ``slprop = state -> prop``, where ``state`` represents the +state of a program, e.g., the contents of memory. It is useful (at least at +first) to think of a ``slprop`` as a memory property, though we will eventually +treat it more abstractly and use it to model many other kinds of resources. .. I'm calling it a hmem to not confuse things with heap vs stack later. -**Separation Logic Hoare Triples** To connect ``vprop``'s to programs, +**Separation Logic Hoare Triples** To connect ``slprop``'s to programs, separation logics use Hoare triples to describe the action of a program on its state. For example, the Hoare triple ``{ p } c { n. q }`` describes a program ``c`` which when run in an initial state @@ -63,15 +62,15 @@ satisfying ``q n s1`` (i.e., ``q`` is a postcondition). Pulse's program logic is a partial-correctness logic, meaning that ``c`` may also loop forever, deadlock with other threads, etc. -**Some simple vprops and triples**: Here are two of the simplest - ``vprops`` (defined in ``Pulse.Lib.Pervasives``): +**Some simple slprops and triples**: Here are two of the simplest + ``slprops`` (defined in ``Pulse.Lib.Pervasives``): * ``emp``, the trivial proposition (equivalent to ``fun s -> True``). * ``pure p``, heap-independent predicate ``fun s -> p``. ``emp`` is equivalent to ``pure True``. -The type of the program ``five`` illustrates how these ``vprop``'s are +The type of the program ``five`` illustrates how these ``slprop``'s are used in program specifications: * It is a function with a single unit argument---Pulse functions use @@ -113,12 +112,12 @@ observable way. **Separating Conjunction and the Frame Rule** Let's go back to ``incr`` and ``par_incr`` that we saw in the previous section and look at their types closely. We'll need to introduce two more common -``vprop``'s, starting with the "points-to" predicate: +``slprop``'s, starting with the "points-to" predicate: * ``pts_to x v`` asserts that the reference ``x`` points to a cell in the current state that holds the value ``v``. -``vprop``'s can also be combined in various ways, the most common one +``slprop``'s can also be combined in various ways, the most common one being the "separating conjunction", written ``**`` in Pulse. [#]_ * ``p ** q``, means that the state can be split into two *disjoint* @@ -138,7 +137,7 @@ being the "separating conjunction", written ``**`` in Pulse. [#]_ Now, perhaps the defining characteristic of separation logic is how the ``**`` operator works in the program logic, via a key rule known as the *frame* rule. The rule says that if you can prove the Hoare -triple ``{ p } c { n. q }``, then, for any other ``f : vprop``, you +triple ``{ p } c { n. q }``, then, for any other ``f : slprop``, you can also prove ``{ p ** f } c { n. q ** f }``---``f`` is often called the "frame". It might take some time to appreciate, but the frame rule captures the essence of local, modular reasoning. Roughly, it states @@ -164,7 +163,7 @@ that ``y`` is unchanged. :start-after: //incr_frame$ :end-before: //end incr_frame$ -In fact, Pulse lets us use the frame rule with any ``f:vprop``, and we +In fact, Pulse lets us use the frame rule with any ``f:slprop``, and we get, for free, that ``incr x`` does not disturb ``f``. .. literalinclude:: ../code/pulse/PulseTutorial.Intro.fst @@ -184,9 +183,9 @@ this is equivalent: :start-after: //incr_explicit_i$ :end-before: //end incr_explicit_i$ -**Other vprop connectives** In addition the separating conjunction, +**Other slprop connectives** In addition the separating conjunction, Pulse, like other separation logics, provides other ways to combine -``vprops``. We'll look at these in detail in the subsequent chapters, +``slprops``. We'll look at these in detail in the subsequent chapters, but we list the most common other connectives below just to give you a taste of the logic. @@ -196,7 +195,7 @@ taste of the logic. is valid in a state ``s`` if there is a witness ``w`` such that ``p [w/x]`` is valid in ``s``. For experts, existential quantification is impredicative, in the sense that one can quantify - over ``vprops`` themselves, i.e., ``exists* (p:vprop). q`` is + over ``slprops`` themselves, i.e., ``exists* (p:slprop). q`` is allowed. * ``forall* (x1:t1) ... (xn:tn). p``: Universal quantification is @@ -209,19 +208,11 @@ taste of the logic. separation logics. Pulse does not yet provide libraries for conjunction or -disjunction. However, since Pulse is embedded in F*, new vprops can +disjunction. However, since Pulse is embedded in F*, new slprops can also be defined by the user and it is common to do so, e.g., recursively defined predicates, or variants of the connectives described above. -.. [#] They are called ``vprop`` for mostly historical reasons. A - version of the Steel separation logic from which Pulse grew is - based allows associating a *value* with a separation logic - proposition, so these came to be known as "value" - propositions. However, Pulse does not make use of this - feature---perhaps a better name in Pulse would be ``slprop``, - for separation logic proposition. - .. [#] For experts, Pulse's separation logic is *affine*. .. [#] When we get to things like invariants and locks, we'll see how diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index 89e74cf9299..83dc2c970db 100644 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -125,8 +125,8 @@ Inspecting the proof state .......................... A Pulse program is checked one stateful operation at a time, "pushing -through" the ``vprop`` assertions starting with the precondition, -until the end of function's body. The inferred ``vprop`` at the exit +through" the ``slprop`` assertions starting with the precondition, +until the end of function's body. The inferred ``slprop`` at the exit of a function must match the annotated postcondition. Along the way, the Pulse checker will make several calls to the SMT solver to prove that, say, ``pts_to x (v + v)`` is equal to ``pts_to x (2 * v)``. @@ -139,7 +139,7 @@ state, which has two components: those variables in scope, e.g., ``x:int; y:erased int; _:squash (x == reveal y)``. * A separation logic context, called just "the context", or - sometimes "the ``vprop`` context". The context contains all known + sometimes "the ``slprop`` context". The context contains all known facts about the current state of the program. Pulse provides a command called ``show_proof_state`` that allows the @@ -189,7 +189,7 @@ The comments show how the proof state evolves after each command. automatically, e.g., at the second call to ``add``, Pulse automatically instantiates ``'v`` to ``v2``. - * Pulse automatically moves any ``pure p`` property in the ``vprop`` + * Pulse automatically moves any ``pure p`` property in the ``slprop`` context to a ``squash p`` hypothesis in the typing environment. Pulse also proves ``pure`` properties automatically, by sending queries to the SMT solver, which can make use of the @@ -247,7 +247,7 @@ The full type of the ``pts_to`` predicate is shown below: .. code-block:: fstar - val pts_to (#a:Type u#0) (r:ref a) (#p:perm) (v:a) : vprop + val pts_to (#a:Type u#0) (r:ref a) (#p:perm) (v:a) : slprop We have so far been writing ``pts_to r v`` instead of ``pts_to #a r #p v``. Usually, one does not need to write the first argument ``#a`` @@ -353,7 +353,7 @@ A few additional points to note here: * Pulse proves ``pure`` properties automatically, by sending queries to the SMT solver. - * Pulse simplifies ``vprop`` implicitly, e.g., Pulse will + * Pulse simplifies ``slprop`` implicitly, e.g., Pulse will automatically rewrite ``emp ** p`` to ``p``. * Like F*, Pulse tries to instantiate implicit arguments diff --git a/book/pulse/pulse_conditionals.rst b/book/pulse/pulse_conditionals.rst index 5852b7e7870..6d8a6acf96b 100644 --- a/book/pulse/pulse_conditionals.rst +++ b/book/pulse/pulse_conditionals.rst @@ -96,7 +96,7 @@ Note, one might consider defining it this way: (x:nullable_ref a) (#[default_arg (`1.0R)] p:perm) (v:option a) - : vprop + : slprop = match x with | None -> pure (v == None) | Some x -> pure (Some? v) ** pts_to x #p (Some?.v v) @@ -114,7 +114,7 @@ Another style might be as follows: (x:nullable_ref a) (#[default_arg (`1.0R)] p:perm) (v:option a) - : vprop + : slprop = match x, v with | None, None -> emp | Some x, Some w -> pts_to x #p w @@ -186,7 +186,7 @@ path conditions. Helpers +++++++ -When a ``vprop`` is defined by cases (like ``pts_to_or_null``) it is +When a ``slprop`` is defined by cases (like ``pts_to_or_null``) it is very common to have to reason according to those cases when pattern matching. Instead of rewriting, unfolding, folding, and rewriting every time, one can define helper functions to handle these cases. diff --git a/book/pulse/pulse_existentials.rst b/book/pulse/pulse_existentials.rst index 3b731929578..481c4400b0d 100644 --- a/book/pulse/pulse_existentials.rst +++ b/book/pulse/pulse_existentials.rst @@ -12,7 +12,7 @@ As mentioned in the :ref:`introduction to Pulse `, one of the connectives of Pulse's separation logic is the existential quantifier. Its syntax is similar to F*'s existential quantifier, except it is written ``exists*`` instead of just ``exists``, and its -body is a ``vprop``, as in the examples shown below. +body is a ``slprop``, as in the examples shown below. .. code-block:: pulse @@ -53,7 +53,7 @@ value ``w``. Usually, however, the postcondition of a function *relates* the initial state prior to the call to the state after the call and existential variables are only in scope as far to the right as -possible of the enclosing ``vprop``. So, existential quantifiers in +possible of the enclosing ``slprop``. So, existential quantifiers in the precondition of a function are not so common. To illustrate, the following attempted specification of ``incr`` does @@ -151,7 +151,7 @@ Introducing existentials The Pulse checker will automatically introduce existential formulas by introduces new unification variables for each existentially bound variable, and then trying to find solutions for those variables by -matching ``vprops`` in the goal with those in the context. +matching ``slprops`` in the goal with those in the context. However, one can also introduce existential formulas explicitly, using the ``introduce exists*`` syntax, as seen in the two examples diff --git a/book/pulse/pulse_extraction.rst b/book/pulse/pulse_extraction.rst index 04797e58903..b943f8d1216 100644 --- a/book/pulse/pulse_extraction.rst +++ b/book/pulse/pulse_extraction.rst @@ -321,7 +321,7 @@ and the extracted ``majority`` function looks like: .. .. the index is within the array bounds---the refinement ``SZ.v i < .. .. Seq.length s`` enforces that the index is in bounds, where ``module SZ .. .. = FStar.SizeT``. The function returns the ``i``-th element of the -.. .. array, the asserted by the postcondition vprop ``pure (x == Seq.index +.. .. array, the asserted by the postcondition slprop ``pure (x == Seq.index .. .. s (SZ.v i))``. The body of the function uses the array read operator .. .. ``arr.(i)``. diff --git a/book/pulse/pulse_ghost.rst b/book/pulse/pulse_ghost.rst index 5f53dc28f77..be9cb45f01a 100755 --- a/book/pulse/pulse_ghost.rst +++ b/book/pulse/pulse_ghost.rst @@ -15,10 +15,10 @@ erased by the F* compiler. As we've seen already, F* lemmas and ghost functions can be directly used in Pulse code. But, these are only useful for describing properties about the pure values in scope. Often, in Pulse, one needs -to write lemmas that speak about the state, manipulate ``vprops``, +to write lemmas that speak about the state, manipulate ``slprops``, etc. For this purpose, Pulse provides its own notion of *ghost computations* (think of these as the analog of F* lemmas and ghost -functions, except they are specified using ``vprops``); and *ghost +functions, except they are specified using ``slprops``); and *ghost state* (think of these as the analog of F* erased types, except ghost state is mutable, though still computationally irrelevant). Ghost computations are used everywhere in Pulse---we've already seen a few @@ -136,7 +136,7 @@ postconditions are not that interesting---such functions can usually be written with regular F* ghost functions. Ghost functions are often used as proof steps to prove equivalences -among ``vprops``. We saw a few :ref:`examples of ghost functions +among ``slprops``. We saw a few :ref:`examples of ghost functions before `---they are ghost since their implementations are compositions of ``ghost`` functions from the Pulse library. @@ -170,8 +170,8 @@ Recursive Predicates and Ghost Lemmas We previously saw how to :ref:`define custom predicates `, e.g., for representation predicates on data -structures. Since a ``vprop`` is just a regular type, one can also -define ``vprops`` by recursion in F*. Working with these recursive +structures. Since a ``slprop`` is just a regular type, one can also +define ``slprops`` by recursion in F*. Working with these recursive predicates in Pulse usually involves writing recursive ghost functions as lemmas. We'll look at a simple example of this here and revisit in subsequent chapters as look at programming unbounded structures, like @@ -249,7 +249,7 @@ similar to ``Pulse.Lib.Reference``, in that it provides: * ``GR.ref a``: The main type of ghost references. ``GR.ref`` is an erasable type and is hence considered non-informative. - * ``GR.pts_to (#a:Type0) (r:GR.ref a) (#p:perm) (v:a) : vprop`` is + * ``GR.pts_to (#a:Type0) (r:GR.ref a) (#p:perm) (v:a) : slprop`` is the main predicate provided by the library. Similar to the regular ``pts_to``, the permission index defaults to ``1.0R``. diff --git a/book/pulse/pulse_higher_order.rst b/book/pulse/pulse_higher_order.rst index 3654e09d604..ed3edf5abc2 100755 --- a/book/pulse/pulse_higher_order.rst +++ b/book/pulse/pulse_higher_order.rst @@ -76,10 +76,10 @@ below: .. code-block:: fstar - val stt (a:Type u#a) (i:inames) (pre:vprop) (post: a -> vprop) + val stt (a:Type u#a) (i:inames) (pre:slprop) (post: a -> slprop) : Type u#0 - val stt_ghost (a:Type u#a) (i:inames) (pre:vprop) (post: a -> vprop) + val stt_ghost (a:Type u#a) (i:inames) (pre:slprop) (post: a -> slprop) : Type u#4 A point to note is that ``stt`` computations live in universe diff --git a/book/pulse/pulse_implication_and_forall.rst b/book/pulse/pulse_implication_and_forall.rst index a805193cf45..c05e079728a 100755 --- a/book/pulse/pulse_implication_and_forall.rst +++ b/book/pulse/pulse_implication_and_forall.rst @@ -27,7 +27,7 @@ what ``p @==> q`` means: .. code-block:: pulse ghost - fn I.elim (p q:vprop) + fn I.elim (p q:slprop) requires p ** (p @==> q) ensures q @@ -44,7 +44,7 @@ introduction form, shown below: .. code-block:: pulse ghost - fn I.intro (p q r:vprop) + fn I.intro (p q r:slprop) (elim: unit -> stt_ghost unit emp_inames (r ** p) (fun _ -> q)) requires r ensures p @==> q @@ -91,7 +91,7 @@ corresponds to the eliminator for ``pts_to x #one_half 'v @==> pts_to x halves, call ``I.intro`` passing the eliminator, and the fold it into a ``regain_half``. All ``regain_half`` has done is to package the ghost function ``aux``, together the half permission on ``x``, and put -it into a ``vprop``. +it into a ``slprop``. Later on, if want to use ``regain_half``, we can call its eliminator---which, effectively, calls ``aux`` with the needed @@ -152,7 +152,7 @@ have a similar shape to what we saw earlier for ``@==>``: .. code-block:: pulse ghost - fn FA.elim (#a:Type) (#p:a->vprop) (v:a) + fn FA.elim (#a:Type) (#p:a->slprop) (v:a) requires (forall* x. p x) ensures p v @@ -162,8 +162,8 @@ bound ``x`` to ``v``. .. code-block:: pulse ghost - fn FA.intro (#a:Type) (#p:a->vprop) - (v:vprop) + fn FA.intro (#a:Type) (#p:a->slprop) + (v:slprop) (f_elim : (x:a -> stt_ghost unit emp_inames v (fun _ -> p x))) requires v ensures (forall* x. p x) @@ -177,7 +177,7 @@ together, so the library also provides the following combined forms: .. code-block:: pulse ghost - fn elim_forall_imp (#a:Type0) (p q: a -> vprop) (x:a) + fn elim_forall_imp (#a:Type0) (p q: a -> slprop) (x:a) requires (forall* x. p x @==> q x) ** p x ensures q x @@ -186,7 +186,7 @@ and .. code-block:: pulse ghost - fn intro_forall_imp (#a:Type0) (p q: a -> vprop) (r:vprop) + fn intro_forall_imp (#a:Type0) (p q: a -> slprop) (r:slprop) (elim: (u:a -> stt_ghost unit emp_inames (r ** p u) (fun _ -> q u))) diff --git a/book/pulse/pulse_linked_list.rst b/book/pulse/pulse_linked_list.rst index a15347d6494..20622bbb40c 100755 --- a/book/pulse/pulse_linked_list.rst +++ b/book/pulse/pulse_linked_list.rst @@ -301,9 +301,9 @@ There are few interesting points to note. ghost fn trans_compose (#a #b #c:Type0) - (p: a -> vprop) - (q: b -> vprop) - (r: c -> vprop) + (p: a -> slprop) + (q: b -> slprop) + (r: c -> slprop) (f: a -> GTot b) (g: b -> GTot c) requires diff --git a/book/pulse/pulse_parallel_increment.rst b/book/pulse/pulse_parallel_increment.rst index 005d9df6398..63acc7347f4 100755 --- a/book/pulse/pulse_parallel_increment.rst +++ b/book/pulse/pulse_parallel_increment.rst @@ -379,12 +379,12 @@ do. .. code-block:: pulse ghost - fn unpack_cinv_vp (#p:perm) (#v:vprop) (c:cinv) + fn unpack_cinv_vp (#p:perm) (#v:slprop) (c:cinv) requires cinv_vp c v ** active c p ensures v ** unpacked c ** active c p ghost - fn pack_cinv_vp (#v:vprop) (c:cinv) + fn pack_cinv_vp (#v:slprop) (c:cinv) requires v ** unpacked c ensures cinv_vp c v @@ -395,7 +395,7 @@ obtained as postcondition. .. code-block:: pulse ghost - fn cancel (#v:vprop) (c:cinv) + fn cancel (#v:slprop) (c:cinv) requires inv (iref_of c) (cinv_vp c v) ** active c 1.0R ensures v opens add_inv emp_inames (iref_of c) diff --git a/book/pulse/pulse_spin_lock.rst b/book/pulse/pulse_spin_lock.rst index 8ae6a7bfec9..49e3bfd9103 100755 --- a/book/pulse/pulse_spin_lock.rst +++ b/book/pulse/pulse_spin_lock.rst @@ -105,7 +105,7 @@ Pulse.Lib.Reference: .. code-block:: fstar - let cond b (p q:vprop) = if b then p else q + let cond b (p q:slprop) = if b then p else q .. code-block:: pulse diff --git a/book/pulse/pulse_user_defined_predicates.rst b/book/pulse/pulse_user_defined_predicates.rst index 8d07add8264..3f17b865b14 100644 --- a/book/pulse/pulse_user_defined_predicates.rst +++ b/book/pulse/pulse_user_defined_predicates.rst @@ -3,8 +3,8 @@ User-defined Predicates ======================= -In addition to the vprop predicates and connectives that the Pulse -libraries provide, users very commonly define their own ``vprops``. We +In addition to the slprop predicates and connectives that the Pulse +libraries provide, users very commonly define their own ``slprops``. We show a few simple examples here---subsequent examples will make heavy use of user-defined predicates. For example, see this section for :ref:`recursively defined predicates `. @@ -21,7 +21,7 @@ A simple example of a user-defined abstraction is show below. :start-after: //pts_to_diag$ :end-before: //end pts_to_diag$ -``pts_to_diag r v`` is a ``vprop`` defined in F* representing a +``pts_to_diag r v`` is a ``slprop`` defined in F* representing a reference to a pair whose components are equal. We can use this abstraction in a Pulse program, though we have to be @@ -50,7 +50,7 @@ common cases (such as the ones here) can be easily automated. We are currently investigating adding support for this. Some initial support for this is already available, inasmuch as Pulse -can sometimes figure out the arguments to the vprops that need to be +can sometimes figure out the arguments to the slprops that need to be folded/unfolded. For instance, in the code below, we just mention the name of the predicate to be unfolded/folded, without needing to provide all the arguments. @@ -74,7 +74,7 @@ structure with two mutable integer fields, representing a :end-before: //end point$ A ``point`` is just an F* record containing two -references. Additionally, we define ``is_point``, a ``vprop``, +references. Additionally, we define ``is_point``, a ``slprop``, sometimes called a "representation predicate", for a ``point``. ``is_point p xy`` says that ``p`` is a representation of the logical point ``xy``, where ``xy`` is pure, mathematical pair. @@ -207,7 +207,7 @@ Finally, one can also write: rewrite each e1 as e1', ..., en as en' omitting the ``goal`` term. In this case, the ``goal`` is taken to be -the entire current ``vprop`` context. +the entire current ``slprop`` context. Using ``rewrite each ...`` makes the code somewhat shorter: From cab033323f1ddd464402a6564996e94540686af4 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 6 Apr 2025 20:25:33 -0700 Subject: [PATCH 54/65] revise atomics & invariants chapter to describe impredicativity and later modality --- book/pulse/pulse.rst | 27 +- book/pulse/pulse_atomics_and_invariants.rst | 351 ++++++++++++++------ 2 files changed, 271 insertions(+), 107 deletions(-) diff --git a/book/pulse/pulse.rst b/book/pulse/pulse.rst index 92d90df037f..2b84a6f4047 100644 --- a/book/pulse/pulse.rst +++ b/book/pulse/pulse.rst @@ -44,19 +44,20 @@ returns, "x points to 'i + 1"; while ``par_incr`` increments the contents of ``x`` and ``y`` in parallel by using the ``par`` combinator. -Concurrent separation logic is an active research area and there are -many such logics out there, all with different tradeoffs. Pulse's -logic is based on a logic called PulseCore, formalized entirely within -F*---you can find the formalization `here -`_. Proofs -of programs in Pulse's surface language correspond to proofs of -correctness in the PulseCore program logic. But, you should not need -to know much about how the logic is formalized to use Pulse -effectively. We'll start from the basics and explain what you need to -know about concurrent separation logic to start programming and -proving in Pulse. Additionally, Pulse is an extension of F*, so all -you've learned about F*, lemmas, dependent types, refinement types, -etc. will be of use again. +Concurrent separation logic is an active research area and there are many such +logics to use, all with different tradeoffs. The state of the art in concurrent +separation logic is `Iris `_, a higher-order, +impredicative separation logic. Drawing inspiration from Iris, Pulse's logic is +similar in many ways to Iris, but is based on a logic called PulseCore, +formalized entirely within F*---you can find the formalization `here +`_. Proofs of +programs in Pulse's surface language correspond to proofs of correctness in the +PulseCore program logic. But, you should not need to know much about how the +logic is formalized to use Pulse effectively. We'll start from the basics and +explain what you need to know about concurrent separation logic to start +programming and proving in Pulse. Additionally, Pulse is an extension of F*, so +all you've learned about F*, lemmas, dependent types, refinement types, etc. +will be of use again. .. note:: diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst index 381b9baaa51..f6cf48271d5 100755 --- a/book/pulse/pulse_atomics_and_invariants.rst +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -60,7 +60,7 @@ computation. This is an assumption about the target architecture on which a Pulse program is executed. It may be that on some machines, 32-bit values cannot be read or written atomically. So, when using atomic -operations, you should be careful to check that its safe to assume +operations, you should be careful to check that it is safe to assume that these operations truly are atomic. Pulse also provides a way for you to declare that other operations are @@ -111,53 +111,17 @@ Every ``iref`` can be turned into an ``iname``, with the function ``iname_of (i:iref): GTot iname``. Invariants are duplicable, i.e., from ``inv i p`` one can prove ``inv -i p ** inv i p``, as shown by type of ``Pulse.Lib.Core.dup_inv`` +i p ** inv i p``, as shown by the type of ``Pulse.Lib.Core.dup_inv`` below: -.. code-block:: fstar +.. code-block:: pulse - val dup_inv (i:iref) (p:slprop) - : stt_ghost unit emp_inames (inv i p) (fun _ -> inv i p ** inv i p) - - -Boxable predicates -++++++++++++++++++ - -Pulse's language of predicates, i.e., the type ``slprop``, is -stratified. The type ``boxable`` is a refinement of ``slprop``, defined -as shown below in ``Pulse.Lib.Core`` - -.. code-block:: fstar - - let boxable = v:slprop { is_big v } - -That is, certain ``slprops``, i.e., those that satisfy ``is_big``, are -``boxable`` predicates. All the predicates that we have encountered so -far are boxable, except for the ``inv i p`` predicate. For example, -``pts_to x v`` is boxable; ``exists* x. p x`` is boxable if ``p x`` is -boxable; etc. However ``inv i p`` is not boxable. - -Why does this matter? It turns out that PulseCore, the logic on which -Pulse is built, only allows turning boxable predicates into -invariants. That is, while one can build an invariant such as ``inv i -(exists* v. pts_to x v)``, one **cannot** nest invariants, i.e., there -is no meaningful way to construct an instance of ``inv i (inv j p)``. - -This restriction is a fundamental limitation of PulseCore: invariants -cannot mention other invariants. In more technical terms, *invariants -are predicative*. One might wonder whether this limitation is -significant: after all, why might one want to construct an invariant -that states that some ``p`` is already an invariant? It turns out that -such predicates, although not very common, are useful and the -inability to nest invariants in Pulse makes some styles of proofs -awkward or perhaps even impossible. Nevertheless, forcing invariants -to be predicative gives Pulse a simple foundational model in PulseCore -in terms of a standard, predicative, dependently typed logic. - -Let's look next at how to turn a boxable predicate into an invariant. + ghost fn dup_inv (i:iref) (p:slprop) + requires inv i p + ensures inv i p ** inv i p -Creating an invariant and boxable predicates -++++++++++++++++++++++++++++++++++++++++++++ +Creating an invariant ++++++++++++++++++++++ Let's start by looking at how to create an invariant. @@ -169,9 +133,6 @@ full-permission on ``x``. :start-after: //owns$ :end-before: //end owns$ -Notice the type annotation on ``owns`` claims that it is ``boxable``, -and indeed F*'s refinement type checker automatically proves that it -is. Now, if we can currently prove ``pts_to r x`` then we can turn it into an invariant ``inv i (owns r)``, as shown below. @@ -200,38 +161,55 @@ e.g., by freeing ``r``. we're working to improve. In the meantime, use an auxiliary predicate. -Note, if one were to try to allocate an invariant for a non-boxable predicate, -typechecking fails, as shown in the example below: +Impredicativity and the ``later`` modality ++++++++++++++++++++++++++++++++++++++++++++ -.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst - :language: pulse - :start-after: //create_non_boxable$ - :end-before: //end create_non_boxable$ +Pulse allows *any* predicate ``p:slprop`` to be turned into an invariant ``inv i +p : slprop``. Importantly, ``inv i p`` is itself an ``slprop``, so one can even +turn an invariant into another invariant, ``inv i (inv j p)``, etc. This ability +to turn any predicate into an invariant, including invariants themselves, makes +Pulse an *impredicative* separation logic. -failing with an error pointing to the source location of the -refinement precondition, ``is_big``, at the call to ``new_invariant``. +Impredicativity turns out to be useful for a number of reasons, e.g., one could +create a lock to protect access to a data structure that may itself contain +further locks. However, soundly implementing impredicativity in a separation +logic is challenging, since it involves resolving a kind of circularity in the +definitions of heaps and heap predicates. PulseCore resolves this circularity +using something called *indirection theory*, using it to provide a foundational +model for impredicative invariants, together with all the constructs of Pulse. +The details of this construction is out of scope here, but one doesn't really +need to know how the construction of the model works to use the resulting logic. -.. code-block:: +We provide a bit of intuition about the model below, but for now, just keep in +mind that Pulse includes the following abstract predicates: + +.. code-block:: fstar + + val later (p:slprop) : slprop + val later_credit (i:nat) : slprop - - Assertion failed - - The SMT solver could not prove the query. Use --query_stats for more details. - - See also ../../../lib/pulse/lib/Pulse.Lib.Core.fsti(536,29-536,37) +with the following forms to introduce and eliminate them: -As you can see, although the language does not prevent you from -writing ``inv i p`` for any predicate ``p``, the only way to allocate -an instance of ``inv i p`` is by provable that ``p`` is -``boxable``. This design is convenient since the onus of proving that -a predicate is boxable is only placed at the allocation site of the -invariant---uses of invariants do not need to worry about the -distinction between ``boxable`` and general ``slprops``. +.. code-block:: pulse + + ghost fn later_intro (p: slprop) + requires p + ensures later p + + ghost fn later_elim (p: slprop) + requires later p ** later_credit 1 + ensures p -Opening an invariant -++++++++++++++++++++ + fn later_credit_buy (amt:nat) + requires emp + ensures later_credit n -Now that we've allocated an ``inv i (owns r)``, what can we do with it? -As we said earlier, one can make use of the ``owns r`` in an atomic -computation, so long as we restore it at the end of the atomic -step. +Opening Invariants +++++++++++++++++++++++++++++++++++++++++++++++ + +Once we've allocated an invariant, ``inv i (owns r)``, what can we do with it? +As we said earlier, one can make use of the ``owns r`` in an atomic computation, +so long as we restore it at the end of the atomic step. The ``with_invariants`` construct gives us access to the invariant within the scope of at most one atomic step, preceded or succeeded by @@ -247,7 +225,6 @@ invariants ``i_1`` to ``i_k`` in the scope of ``e``. ensures post { e } - In many cases, the ``returns`` and ``ensures`` annotations are omitted, since it can be inferred. @@ -270,10 +247,10 @@ Here's the rule for opening a single invariant ``inv i p`` using * ``i`` must have type ``iref`` and ``inv i p`` must be provable in the current context, for some ``p:slprop`` -* ``e`` must have the type ``stt_atomic t j (p ** r) (fun x -> p ** s - x)``. [#]_ That is, ``e`` requires and restores the invariant ``p``, - while also transforming ``r`` to ``s x``, all in at most one atomic - step. Further, the ``name_of_inv i`` must not be in the set ``j``. +* ``e`` must have the type ``stt_atomic t j (later p ** r) (fun x -> later p ** + s x)``. [#]_ That is, ``e`` requires and restores ``later p``, while also + transforming ``r`` to ``s x``, all in at most one atomic step. Further, the + ``name_of_inv i`` must not be in the set ``j``. * ``with_invariants i { e }`` has type ``stt_atomic t (add_inv i j) (inv i p ** r) (fun x -> inv i p ** s x)``. That is, ``e`` gets to @@ -290,45 +267,231 @@ Let's look at a few examples to see how ``with_invariants`` works. .. [#] - Alternatively ``e`` may have type ``stt_ghost t j (p ** r) (fun x - -> p ** s x)``, in which case the entire ``with_invariants i { e - }`` block has type ``stt_ghost t (add_inv i j) (inv i p ** r) (fun - x -> inv i p ** s x)``, i.e., one can open an invariant and use it - in either an atomic or ghost context. + Alternatively ``e`` may have type ``stt_ghost t j (later p ** r) (fun x -> + later p ** s x)``, in which case the entire ``with_invariants i { e }`` + block has type ``stt_ghost t (add_inv i j) (inv i p ** r) (fun x -> inv i p + ** s x)``, i.e., one can open an invariant and use it in either an atomic or + ghost context. Updating a reference ~~~~~~~~~~~~~~~~~~~~ -In the example below, given ``inv i (owns r)``, we can atomically -update a reference with a pre- and postcondition of ``emp``. +Let's try do update a reference, given ``inv i (owns r)``. Our first attempt is +shown below: + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //update_ref_atomic0$ + :end-before: //end update_ref_atomic0$ + +We use ``with_invariants i { ... }`` to open the invariant, and in the scope of +the block, we have ``later (owns r)``. Now, we're stuck: we need ``later (owns +r)``, but we only have ``later (owns r)``. In order to eliminate the later, we +can use the ``later_elim`` combinator shown earlier, but to call it, we need to +also have a ``later_credit 1``. + +So, let's try again: .. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst :language: pulse :start-after: //update_ref_atomic$ :end-before: //end update_ref_atomic$ -* At the start of the ``with_invariants`` scope, we have ``owns r`` in +* The precondition of the function also includes a ``later_credit 1``. + +* At the start of the ``with_invariants`` scope, we have ``later (owns r)`` in the context. +* The ghost step ``later_elim _`` uses up the later credit and eliminates + ``later (owns r)`` into ``owns r``. + * The ghost step ``unfold owns`` unfolds it to its definition. * Then, we do a single atomic action, ``write_atomic``. * And follow it up with a ``fold owns``, another ghost step. +* To finish the block, we need to restore ``later (owns r)``, but we have ``owns + r``, so the ghost step ``later_intro`` does the job. + * The block within ``with_invariants i`` has type ``stt_atomic unit - emp_inames (owns r ** emp) (fun _ -> owns r ** emp)`` + emp_inames (later (owns r) ** later_credit 1) (fun _ -> later (owns r) ** emp)`` + +* Since we opened the invariant ``i``, the type of ``update_ref_atomic`` records + this in the ``opens (singleton i)`` annotation; equivalently, the type is + ``stt_atomic unit (singleton i) (inv i (owns r) ** later_credit 1) (fun _ -> + inv i (owns r))``. When the ``opens`` annotation is omitted, it defaults to + ``emp_inames``, the empty set of invariant names. + +Finally, to call ``update_ref_atomic``, we need to buy a later credit first. +This is easily done before we call the atomic computation, as shown below: + + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //update_ref$ + :end-before: //end update_ref$ + +The later modality and later credits +++++++++++++++++++++++++++++++++++++ + +Having seen an example with later modality at work, we provide a bit of +intuition for the underlying model. + +The semantics of PulseCore is defined with respect to memory with an abstract +notion of a "ticker", a natural number counter, initialized at the start of a +program's execution. In other logics, this is sometimes called a "step index", +but in PulseCore, the ticker is unrelated to the number of actual steps a +computation takes. Instead, at specific points in the program, the programmer +can issue a specific *ghost* instruction to "tick" the ticker, decreasing its +value by one unit. The decreasing counter provides a way to define an +approximate fixed point between the otherwise-circular heaps and heap +predicates. The logic is defined in such a way that it is always possible to +pick a high enough initial value for the ticker so that any finite number of +programs steps can be executed before the ticker is exhausted. + +Now, rather than explicitly working with the ticker, PulseCore encapsulates all +reasoning about the ticker using two logical constructs: the *later* modality +and *later credits*, features found in Iris and other separation logics that +feature impredicativity. + +The Later Modality and Later Credits +------------------------------------- + +The predicate ``later p`` states that the ``p:slprop`` is true after one tick. + +.. code-block:: pulse + + val later (p: slprop) : slprop + +All predicates ``p:slprop`` are "hereditary", meaning that if they are true in a +given memory, then they are also true after that memory is ticked. The ghost +function ``later_intro`` embodies this principle: from ``p`` one can prove +``later p``. + +.. code-block:: pulse + + ghost fn later_intro (p: slprop) + requires p + ensures later p + +Given a ``later p``, one can prove ``p`` by using ``later_elim``. This ghost +function effectively "ticks" the memory (since ``later p`` says that ``p`` is +true after a tick), but in order to do so, it needs a precondition that the +ticker has not already reached zero: ``later_credit 1`` says just that, i.e., +that the memory can be ticked at least once. + +.. code-block:: pulse + + ghost fn later_elim (p: slprop) + requires later p ** later_credit 1 + ensures p + +The only way to get a ``later_credit 1`` is to *buy* a credit with the operation +below---this is a concrete operation that ensures that the memory can be ticked +at least ``n`` times. + +.. code-block:: pulse + + fn later_credit_buy (amt:nat) + requires emp + ensures later_credit n + +At an abstract level, if the ticker cannot be ticked further, the program loops +indefinitely---programs that use later credits (and more generally in step +indexed logics) are inherently proven only partially correct and are allowed to +loop infinitely. At a meta-level, we show that one can always set the initial +ticker value high enough that ``later_credit_buy`` will never actually loop +indefinitely. In fact, when compiling a program, Pulse extracts +``later_credit_buy n`` to a noop ``()``. + +Note, later credits can also be split and combined additively: + +.. code-block:: fstar + + val later_credit_zero () + : Lemma (later_credit 0 == emp) + + val later_credit_add (a b: nat) + : Lemma (later_credit (a + b) == later_credit a ** later_credit b) + +Timeless Predicates +------------------- + +All predicates ``p:slprop`` are hereditary, meaning that ``p`` implies ``later +p``. Some predicates, including many common predicates like ``pts_to`` are also +**timeless**, meaning that ``later p`` implies ``p``. Combining timeless +predicates with ``**`` or exisentially quantifying over timeless predicates +yields a timeless predicate. + +All of the following are available in Pulse.Lib.Core: + +.. code-block:: fstar + + val timeless (p: slprop) : prop + let timeless_slprop = v:slprop { timeless v } + val timeless_emp : squash (timeless emp) + val timeless_pure (p:prop) : Lemma (timeless (pure p)) + val timeless_star (p q : slprop) : Lemma + (requires timeless p /\ timeless q) + (ensures timeless (p ** q)) + val timeless_exists (#a:Type u#a) (p: a -> slprop) : Lemma + (requires forall x. timeless (p x)) + (ensures timeless (op_exists_Star p)) + +And in Pulse.Lib.Reference, we have: + +.. code-block:: fstar + + val pts_to_timeless (#a:Type) (r:ref a) (p:perm) (x:a) + : Lemma (timeless (pts_to r #p x)) + [SMTPat (timeless (pts_to r #p x))] + +For timeless predicates, the ``later`` modality can be eliminated trivially +without requiring a credit. + +.. code-block:: pulse + + ghost fn later_elim_timeless (p: timeless_slprop) + requires later p + ensures p + +Updating a reference, with timeless predicates +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Since ``pts_to`` is timeless, we can actually eliminate ``later (owns r)`` +without a later credit, as shown below. + +First, we prove that ``owns`` is timeless: + + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //owns_timeless$ + :end-before: //end owns_timeless$ + +.. note:: + + It's usually easier to prove a predicate timeless by just annotating its + definition, rather than writing an explicit lemma. For example, + this would have worked: + + .. code-block:: fstar + + let owns (x:ref U32.t) : timeless_slprop = exists* v. pts_to x v + +Next, we can revise ``update_ref_atomic`` to use ``later_elim_timeless``, rather +than requiring a later credit. + +.. literalinclude:: ../code/pulse/PulseTutorial.AtomicsAndInvariants.fst + :language: pulse + :start-after: //update_ref_atomic_alt$ + :end-before: //end update_ref_atomic_alt$ -* Since we opened the invariant ``i``, the type of - ``update_ref_atomic`` records this in the ``opens (singleton i)`` - annotation; equivalently, the type is ``stt_atomic unit - (singleton i) (inv i (owns r)) (fun _ -> inv i (owns r))``. When the - ``opens`` annotation is omitted, it defaults to ``emp_inames``, the - empty set of invariant names. Double opening is unsound -~~~~~~~~~~~~~~~~~~~~~~~~~ +++++++++++++++++++++++++++ To see why we have to track the names of the opened invariants, consider the example below. If we opened the same invariant twice @@ -345,7 +508,7 @@ a contradiction. Subsuming atomic computations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +++++++++++++++++++++++++++++++ Atomic computations can be silently converted to regular, ``stt`` computations, while forgetting which invariants they opened. For From 8db8bbcc6a0bad935e630c1b39c6c0b7ecb541b4 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Sun, 6 Apr 2025 20:35:29 -0700 Subject: [PATCH 55/65] update spin lock chapter to not mention boxable predicates --- book/pulse/pulse_spin_lock.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/book/pulse/pulse_spin_lock.rst b/book/pulse/pulse_spin_lock.rst index 49e3bfd9103..54e24dfa259 100755 --- a/book/pulse/pulse_spin_lock.rst +++ b/book/pulse/pulse_spin_lock.rst @@ -42,7 +42,6 @@ The predicate ``lock_inv r p`` states: The lock itself pairs the concrete mutable state ``box U32.t`` with an invariant reference ``i:iref``, where the ``lock_alive l p`` predicate states that ``l.i`` names an invariant for ``lock_alive l.r p``. - Creating a lock ............... @@ -56,8 +55,6 @@ allocated ``l:lock`` :start-after: //new_lock$ :end-before: //end new_lock$ -Importantly, since allocating a lock involves allocating an invariant -that protects the predicate ``p``, we need ``p`` to be ``boxable``. Some notes on the implementation: From 5e5afdeb32ebd7eb1d11affc42cafce962f2857b Mon Sep 17 00:00:00 2001 From: TJNhxMZHmqGytuWT <113251528+TJNhxMZHmqGytuWT@users.noreply.github.com> Date: Wed, 21 May 2025 02:03:05 +0200 Subject: [PATCH 56/65] Fix a few typos --- book/intro.rst | 8 ++++---- book/part1/part1_getting_off_the_ground.rst | 8 ++++---- book/part1/part1_inductives.rst | 4 ++-- book/part1/part1_polymorphism.rst | 6 +++--- book/part1/part1_prop_assertions.rst | 14 +++++++------- book/part1/part1_termination.rst | 4 ++-- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/book/intro.rst b/book/intro.rst index 6f0c8973883..d2443c3a567 100644 --- a/book/intro.rst +++ b/book/intro.rst @@ -108,7 +108,7 @@ inductively defined definitions of types. :start-after: SNIPPET_START: vec :end-before: SNIPPET_END: vec -Operations on a vectors can be given types that describe their +Operations on vectors can be given types that describe their behavior in terms of vector lengths. For example, here's a recursive function ``append`` to concatenate two @@ -381,7 +381,7 @@ some of the following: in Idris. * `Theorem Proving in Lean - `_: This is + `_: This is the standard reference for learning about the Lean theorem prover, though there are several other `resources `_ too. @@ -432,12 +432,12 @@ tactics are the primary way of conducting proofs. Other dependently typed languages shine in their usage in formalizing mathematics---Lean's `mathlib -`_ and Coq's +`_ and Coq's `Mathematical Components `_ are two great examples. In comparison, to date, relatively little pure mathematics has been formalized in F*. Rather, F*, with its focus on effectful programming and compilation to mainstream languages like C, -has been used to it produce industrial-grade high-assurance software, +has been used to produce industrial-grade high-assurance software, deployed in settings like the `Windows `_ and `Linux `_ kernels, among `many diff --git a/book/part1/part1_getting_off_the_ground.rst b/book/part1/part1_getting_off_the_ground.rst index f14fd8b89a0..8be615b8121 100644 --- a/book/part1/part1_getting_off_the_ground.rst +++ b/book/part1/part1_getting_off_the_ground.rst @@ -16,7 +16,7 @@ F* can be used as a command line tool with any text editor. If you're viewing this in the interactive online tutorial, you can use the `Ace-based `_ text editor alongside, which provides some basic conveniences like syntax highlighting. However, -beyond casual use, most users of F* rely on one of the follwing IDE +beyond casual use, most users of F* rely on one of the following IDE plugins. * `fstar-mode.el `_, @@ -304,7 +304,7 @@ provides several ways to define functions, the most basic form is the :math:`\lambda` term, also called a function literal, an anonymous function, or a simply a *lambda*. The syntax is largely inherited from OCaml, and this `OCaml tutorial -`_ +`_ provides more details for those unfamiliar with the language. We'll assume a basic familiarity with OCaml-like syntax. @@ -541,9 +541,9 @@ In response ``fstar`` should output:: All verification conditions discharged successfully This means that F* attempted to verify the module named ``Sample``. In -doing so, it generated a some "verification conditions", or proof +doing so, it generated some "verification conditions", or proof obligations, necessary to prove that the module is type correct, and -that is discharged, or proved, all of them successfully. +it discharged, or proved, all of them successfully. **F\* in emacs** diff --git a/book/part1/part1_inductives.rst b/book/part1/part1_inductives.rst index 0914fb6097c..928df8fbaf4 100644 --- a/book/part1/part1_inductives.rst +++ b/book/part1/part1_inductives.rst @@ -63,7 +63,7 @@ of type ``three``. :start-after: //SNIPPET_START: assert :end-before: //SNIPPET_END: assert -To write functions that case analyze these new types, one uses the +To write functions that can analyze these new types, one uses the ``match`` construct. The syntax of ``match`` in F* is very similar to OCaml or F#. We'll assume that you're familiar with its basics. As we go, we'll learn about more advanced ways to use ``match``. @@ -208,7 +208,7 @@ Records ....... A record is just a tuple with user-chosen names for its fields and -with special syntax for constructing then and projecting their +with special syntax for constructing them and projecting their fields. Here's an example. .. literalinclude:: ../code/Part1.Inductives.fst diff --git a/book/part1/part1_polymorphism.rst b/book/part1/part1_polymorphism.rst index 01d90005596..497bfe735f4 100644 --- a/book/part1/part1_polymorphism.rst +++ b/book/part1/part1_polymorphism.rst @@ -38,7 +38,7 @@ here's a polymorphic identity function:: let id : a:Type -> a -> a = fun a x -> x -There are a several things to note here: +There are several things to note here: * The type of ``id`` is an arrow type, with two arguments. The first argument is ``a : Type``; the second argument is a term of type @@ -129,7 +129,7 @@ leave out type annotations (e.g., on variables, or when using type-polymorphic (aka generic) functions) and the compiler determines an appropriate type based on the surrounding program context. F*'s type inference includes such a feature, but is considerably more -powerful. Like in other dependently typed language, F*'s inference +powerful. Like in other dependently typed languages, F*'s inference engine is based on `higher-order unification `_ and can be used to infer arbitrary fragments of program text, not just @@ -157,7 +157,7 @@ with an underscore ``_``. :start-after: //SNIPPET_START: implicit id applications :end-before: //SNIPPET_END: implicit id applications -The underscore symbols is a wildcard, or a hole in program, and it's +The underscore symbol is a wildcard, or a hole in program, and it's the job of the F* typechecker to fill in the hole. .. note:: diff --git a/book/part1/part1_prop_assertions.rst b/book/part1/part1_prop_assertions.rst index 7eecbfed302..456d9ef2be2 100644 --- a/book/part1/part1_prop_assertions.rst +++ b/book/part1/part1_prop_assertions.rst @@ -37,11 +37,11 @@ This design has many important consequences, including, briefly: * Undecidability: Since Z3 can check the validity of formulas in the entirety of its logic, including things like quantifying universally - and existentially over infinite ranges, F* does not restrict the the + and existentially over infinite ranges, F* does not restrict the formulas checked for validity by Z3 to be boolean, or even decidable. Yes, typechecking in F* is undecidable. -In this chapter, we'll learn about the the classical logic parts of +In this chapter, we'll learn about the classical logic parts of F*, i.e., the parts that allow it to interface with an SMT solver. .. note:: @@ -49,10 +49,10 @@ F*, i.e., the parts that allow it to interface with an SMT solver. The beginning of this chapter is a little technical, even though we're not telling the full story behind F*'s classical logic yet. If parts of it are hard to understand right now, here's what - you need to know to before you :ref:`jump ahead + you need to know before you :ref:`jump ahead `. - F* let's you write quantified formulas, called propositions, like + F* lets you write quantified formulas, called propositions, like so .. code-block:: fstar @@ -179,7 +179,7 @@ the two propositions below. exists (x:nat). x < 0 The first formula is valid by considering ``x = -1``, while the second -one is not—there is not natural number less than zero. +one is not—there is no natural number less than zero. It is possible to quantify over any F* type. This makes the quantifiers higher order and dependent. For example, one can write @@ -281,7 +281,7 @@ proposition. Propositional equality provides a convenient way to turn a boolean into a proposition. For any boolean ``b``, then term ``b == true`` is -a ``prop``. One seldom needs to do write this manually (although it +a ``prop``. One seldom needs to write this manually (although it does come up occasionally), since F* will automatically insert a ``b==true`` if you're using a ``b:bool`` in a context where a ``prop`` was expected. @@ -382,7 +382,7 @@ for those ``x`` that also validate the preceding assumption. Just like an ``assert``, the type of ``assume p`` is ``unit``. There's a more powerful form of assumption, called an ``admit``. The -term ``admit()`` can given any type you like. For example, +term ``admit()`` can be given any type you like. For example, .. code-block:: fstar diff --git a/book/part1/part1_termination.rst b/book/part1/part1_termination.rst index f8b0fcae26d..02cdf5a5929 100644 --- a/book/part1/part1_termination.rst +++ b/book/part1/part1_termination.rst @@ -17,7 +17,7 @@ to :ref:`compute the length of list ` and to :ref:`append two lists `. We also said :ref:`earlier ` that all functions in F*'s core are *total*, i.e., they always return in a finite amount of time. So, you -may be wondering, what is it that guarantees that recursive function +may be wondering, what is it that guarantees that recursive functions like ``length`` and ``append`` actually terminate on all inputs? The full details of how F* ensures termination of all functions in its @@ -116,7 +116,7 @@ the result type of ``length``, in full detail, we write returning a ``nat``, just as the ``nat`` did before. * The additional ``(decreases l)`` specifying a *measure*, i.e., the - quantity that decreases at each recursive call according the + quantity that decreases at each recursive call according to the well-founded relation ``<<``. To check the definition, F* gives the recursively bound name From fe1556261188349a1aeaad9b0d243a3e882ca3d6 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Fri, 23 May 2025 17:43:52 -0700 Subject: [PATCH 57/65] Part3 chapter: Fun with typeclasses --- book/part3/part3.rst | 3 +- book/part3/part3_alacarte.rst | 446 ++++++++++++++++++++ book/part3/part3_typeclasses.rst | 2 + book/pulse/pulse_atomics_and_invariants.rst | 4 +- 4 files changed, 452 insertions(+), 3 deletions(-) create mode 100644 book/part3/part3_alacarte.rst diff --git a/book/part3/part3.rst b/book/part3/part3.rst index f20134ef009..4cc349eaa3c 100644 --- a/book/part3/part3.rst +++ b/book/part3/part3.rst @@ -54,4 +54,5 @@ most of F*'s typeclass system. :caption: Contents: part3_interfaces - part3_typeclasses + part3_typeclasses + part3_alacarte diff --git a/book/part3/part3_alacarte.rst b/book/part3/part3_alacarte.rst new file mode 100644 index 00000000000..7b15e432303 --- /dev/null +++ b/book/part3/part3_alacarte.rst @@ -0,0 +1,446 @@ +.. _Part3_alacarte: + +Fun with Typeclasses: Datatypes a la Carte +=========================================== + +In a classic 1998 post, Phil Wadler describes a difficulty in language and +library design: how to modularly extend a data type together with the operations +on those types. Wadler calls this the `Expression Problem +`_, +saying: + + The Expression Problem is a new name for an old problem. The goal is to + define a datatype by cases, where one can add new cases to the datatype and + new functions over the datatype, without recompiling existing code, and while + retaining static type safety (e.g., no casts). + +There are many solutions to the Expression Problem, though a particularly +elegant one is Wouter Swierstra's `Data Types a la Carte +`_. +Swierstra's paper is a really beautiful functional pearl and is highly +recommended---it's probably useful background to have before diving into this +chapter, though we'll try to explain everything here as we go. His solution is a +great illustration of extensibility with typeclasses: so, we show how to apply +his approach using typeclasses in F*. More than anything, it's a really fun +example to work out. + +Swierstra's paper uses Haskell: so he does not prove his functions terminating. +One could do this in F* too, using the effect of :ref:`divergence `. +However, in this chapter, we show how to make it all work with total functions +and strictly positive inductive definitions. As a bonus, we also show how to do +proofs of correctness of the various programs that Swierstra develops. + +Getting Started +--------------- + +To set the stage, consider the following simple type of arithmetic expressions +and a function ``evaluate`` to evaluate an expression to an integer: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: exp$ + :end-before: //SNIPPET_END: exp$ + +This is straightforward to define, but it has an extensibility problem. + +If one wanted to add another type of expression, say ``Mul : exp -> exp -> +exp``, then one needs to redefine both the type ``exp`` adding the new case and +to redefine ``evaluate`` to handle that case. + +A solution to the Expression Problem would allow one to add cases to the ``exp`` +type and to progressively define functions to handle each case separately. + +Swierstra's idea is to define a single generic data type that is parameterized +by a type constructor, allowing one to express, in general, a tree of finite +depth, but one whose branching structure and payload is left generic. A first +attempt at such a definition in F* is shown below: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: expr-fail$ + :end-before: //SNIPPET_END: expr-fail$ + +Unfortunately, this definition is not accepted by F*, because it is not +necessarily well-founded. As we saw in a previous section on :ref:`strictly +positive definitions `, if we're not +careful, such definitions can allow one to prove ``False``. In particular, we +need to constrain the type constructor argument ``f`` to be *strictly positive*, +like so: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: expr$ + :end-before: //SNIPPET_END: expr$ + +This definition may bend your mind a little at first, but it's actually quite +simple. It may help to consider an example: the type ``expr list`` has values of +the form ``In of list (expr list)``, i.e., trees of arbitrary depth with a +variable branching factor such as in the example shown below. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: elist$ + :end-before: //SNIPPET_END: elist$ + +Now, given two type constructors ``f`` and ``g``, one can take their *sum* or +coproduct. This is analogous to the ``either`` type we saw in :ref:`Part 1 +`, but at the level of type constructors rather than types: we write +``f ++ g`` for ``coprod f g``. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: coprod$ + :end-before: //SNIPPET_END: coprod$ + +Now, with these abstractions in place, we can define the following, where ``expr +(value ++ add)`` is isomorphic to the ``exp`` type we started with. Notice that +we've now defined the cases of our type of arithmetic expressions independently +and can compose the cases with ``++``. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: value add$ + :end-before: //SNIPPET_END: value add$ + +Of course, building a value of type ``expr (value ++ add)`` is utterly horrible: +but we'll see how to make that better using typeclasses, next . + +Smart Constructors with Injections and Projections +-------------------------------------------------- + +A data constructor, e.g., ``Inl : a -> either a b`` is an injective function +from ``a`` to ``either a b``, i.e., each element ``x:a`` is mapped to a unique +element ``Inl x : either a b``. One can also project back that ``a`` from an +``either a b``, though this is only a partial function. Abstracting injections +and projections will give us a generic way to construct values in our extensible +type of expressions. + +First, we define some abbreviations: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: inj_proj$ + :end-before: //SNIPPET_END: inj_proj$ + +A type constructor ``f`` is less than or equal to another constructor ``g`` if +there is an injection from ``f a`` to ``g a``. This notion is captured by the +typeclass below: We have an ``inj`` and a ``proj`` where ``proj`` is an inverse +of ``inj``, and ``inj`` is a partial inverse of ``proj``. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: leq$ + :end-before: //SNIPPET_END: leq$ + +We can now define some instances of ``leq``. First, of course, ``leq`` is +reflexive, and F* can easily prove the inversion lemma with SMT. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: leq_refl$ + :end-before: //SNIPPET_END: leq_refl$ + +More interestingly, we can prove that ``f`` is less than or equal to the +extension of ``f`` with ``g`` on the left: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: leq_ext_left$ + :end-before: //SNIPPET_END: leq_ext_left$ + +We could also prove the analogous ``leq_ext_right``, but we will explicitly not +give an instance for it, since as we'll see shortly, the instances we give are +specifically chosen to allow type inference to work well. Additional instances +will lead to ambiguities and confuse the inference algorithm. + +Instead, we will give a slightly more general form, including a congruence rule +that says that if ``f`` is less than or equal to ``h``, then ``f`` is also less +than or equal to the extension of ``h`` with ``g`` on the right. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: leq_cong_right$ + :end-before: //SNIPPET_END: leq_cong_right$ + +Now, for any pair of type constructors that satisfy ``leq f g``, we can lift the +associated injections and projections to our extensible expression datatype and +prove the round-trip lemmas + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: inject project$ + :end-before: //SNIPPET_END: inject project$ + +Now, with this machinery in place, we get to the fun part. For each of the cases +of the ``expr`` type, we can define a generic smart constructor, allowing one to +lift it to any type more general than the case we're defining. + +For instance, the smart constructor ``v`` lifts the constructor ``Val x`` into +the type ``expr f`` for any type greater than or equal to ``value``. Likewise, +``(+^)`` lifts ``Add x y`` into any type greater than or equal to ``add``. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: v and +^$ + :end-before: //SNIPPET_END: v and +^$ + +And now we can write our example value so much more nicely than before: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: ex1$ + :end-before: //SNIPPET_END: ex1$ + +The type annotation on ``ex1 : expr (value ++ add)`` is crucial: it allows the +type inference algorithm to instantiate the generic parameter ``f`` in each +``v`` and in ``(+^)`` to ``(value ++ add)`` and then the search for typeclass +instances finds ``value `leq` (value ++ add)`` by using ``leq_cong_right`` and +``leq_left``; and ``add `leq` (value ++ add)`` using ``leq_ext_left``. + +With this setup, extensibility works out smoothly: we can add a multiplication +case, define a smart constructor for it, and easily use it to build expressions +with values, addition, and multiplication. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: mul$ + :end-before: //SNIPPET_END: mul$ + +Evaluating Expressions +---------------------- + +Now that we have a way to construct expressions, let's see how to define an +interpreter for expressions in an extensible way. An interpreter involves +traversing the expression tree, and applying operations to an accumulated +result, and returning the final accumulated value. In other words, we need a way +to *fold* over an expression tree, but to do so in an extensible, generic way. + +The path to doing that involves defining a notion of a functor: we saw functors +briefly in a :ref:`previous section `, and maybe you're +already familiar with it from Haskell. + +Our definition of functor below is slightly different than what one might +normally see. Usually, a type constructor ``t`` is a functor if it supports an +operation ``fmap: (a -> b) -> t a -> t b``. In our definition below, we flip the +order of arguments and require ``fmap x f`` to guarantee that it calls ``f`` +only on subterms of ``x``---this will allow us to build functors over +inductively defined datatypes in an extensible way, while still proving that all +our functions termination. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: functor$ + :end-before: //SNIPPET_END: functor$ + +Functor instances for ``value``, ``add``, and ``mul`` are easy to define: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: functor_value$ + :end-before: //SNIPPET_END: functor_value$ + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: functor_add$ + :end-before: //SNIPPET_END: functor_add$ + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: functor_mul$ + :end-before: //SNIPPET_END: functor_mul$ + +Maybe more interesting is a functor instance for co-products, or sums of +functors, i.e., if ``f`` and ``g`` are both functors, then so is ``f ++ g``. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: functor_coprod$ + :end-before: //SNIPPET_END: functor_coprod$ + +With this in place, we can finally define a generic way to fold over an +expression. Given a function ``alg`` to map an ``f a`` to a result value ``a``, +``fold_expr`` traverses an ``expr f`` accumulating the results of ``alg`` +applied to each node in the tree. Here we see why it was important to refine the +type of ``fmap`` with the precondition ``x << t``: the recursive call to +``fold_expr`` terminates only because the argument ``x`` is guarantee to precede +``t`` in F*'s built-in well-founded order. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: fold_expr$ + :end-before: //SNIPPET_END: fold_expr$ + +Now that we have a general way to fold over our expression trees, we need an +extensible way to define the evaluators for each type of node in a tree. For +that, we can define another typeclass, ``eval f`` for an evaluator for nodes of +type ``f``. It's easy to give instances of eval for our three types of nodes, +separately from each other. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: eval$ + :end-before: //SNIPPET_END: eval$ + +With evaluators for ``f`` and ``g``, one can build an evaluator for ``f++g``. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: eval_coprod$ + :end-before: //SNIPPET_END: eval_coprod$ + +Finally, we can build a generic evaluator for expressions: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: eval_expr$ + :end-before: //SNIPPET_END: eval_expr$ + +And, hooray, it works! We can ask F* to normalize and check that the result +matches what we expect: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: eval_test$ + :end-before: //SNIPPET_END: eval_test$ + +Provably Correct Optimizations +------------------------------ + +Now, let's say we wanted to optimize our expressions, rewriting them by +appealing to the usual arithmetic rules, e.g., distributing multiplication over +addition etc. Swierstra shows how to do that, but in Haskell, there aren't any +proofs of correctness. But, in F*, we can prove our expression rewrite rules +correct, in the sense that they preserve the semantics of expression evaluation. + +Let's start by defining the type of a rewrite rule and what it means for it to +be sound: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: rewrite_rule$ + :end-before: //SNIPPET_END: rewrite_rule$ + +A rewrite rule may fail, but if it rewrites ``x`` to ``y``, then both ``x`` and +``y`` must evaluate to the same result. We can package up a rewrite rule and its +soundness proof into a record, ``rewrite_t``. + +Now, to define some rewrite rules, it's convenient to have a bit of syntax to +handle potential rewrite failures---we'll use the monadic syntax :ref:`shown +previously `. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: error_monad$ + :end-before: //SNIPPET_END: error_monad$ + +Next, in order to define our rewrite rules for each case, we define what we +expect to be true for the expression evaluator for an expession tree that has +that case. + +For instance, if we're evaluating an ``Add`` node, then we expect the result to +the addition of each subtree. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: expected_semantics$ + :end-before: //SNIPPET_END: expected_semantics$ + +We can now define two example rewrite rules. The first rewrites ``(a * (c + +d))`` to ``(a * c + a * d)``; and the second rewrites ``(c + d) * b`` to ``(c * +b + d * b)``. Both of these are easily proven sound for any type of expression +tree whose nodes ``f`` include ``add`` and ``mul``, under the hypothesis that +the evaluator behaves as expected. + +We can generically compose rewrite rules: + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: compose_rewrites$ + :end-before: //SNIPPET_END: compose_rewrites$ + +Then, given any rewrite rule ``l``, we can fold over the expression applying the +rewrite rule bottom up whenever it is eligible. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: rewrite_expr$ + :end-before: //SNIPPET_END: rewrite_expr$ + +As with our evaluator, we can test that it works, by asking F* to evaluate the +rewrite rules on an example. We first define ``rewrite_distr`` to apply both +distributivity rewrite rules. And then assert that rewrite ``ex6`` produces +``ex6'``. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: rewrite_test$ + :end-before: //SNIPPET_END: rewrite_test$ + +Of course, more than just testing it, we can prove that it is correct. In fact, +we can prove that applying any rewrite rule over an entire expression tree +preserves its semantics. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: rewrite_soundness$ + :end-before: //SNIPPET_END: rewrite_soundness$ + +This is the one part of this development where the definition is not completely +generic in the type of expression nodes. Instead, this is proof for the specific +case of expressions that contain values, additions, and multiplications. I +haven't found a way to make this more generic. If you know a way, please let me +know! + +That said, the proof is quite straightforward and pleasant: We simply match on +the cases, use the induction hypothesis on the subtrees if any, and then apply +the soundness lemma of the rewrite rule. F* and Z3 automates much of the +reasoning, e.g., in the last case, we know we must have a ``Mul`` node, since +we've already matched the other two cases. + +Of course, since rewriting is sound for any rule, it is also sound for rewriting +with our distributivity rules. + +.. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: rewrite_distr_soundness$ + :end-before: //SNIPPET_END: rewrite_distr_soundness$ + +Exercises +--------- + +This `file <../code/exercises/Part3.DataTypesALaCarte.fst>`_ provides +the definitions you need. + +Exercise 1 +++++++++++ + +Write a function ``to_string_specific`` whose type is ``expr (value ++ add ++ +mul) -> string`` to print an expression as a string. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: functor$ + :end-before: //SNIPPET_END: functor$ + +Exercise 2 +++++++++++ + +Next, write a class ``render f`` with a ``to_string`` function to generically +print any expression of type ``expr f``. + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: to_string$ + :end-before: //SNIPPET_END: to_string$ diff --git a/book/part3/part3_typeclasses.rst b/book/part3/part3_typeclasses.rst index 3dccca09782..ed6ea0e0547 100644 --- a/book/part3/part3_typeclasses.rst +++ b/book/part3/part3_typeclasses.rst @@ -479,6 +479,8 @@ sufficient. :start-after: //SNIPPET_START: try_sub$ :end-before: //SNIPPET_END: try_sub$ +.. _Part3_monadic_syntax: + Overloading Monadic Syntax -------------------------- diff --git a/book/pulse/pulse_atomics_and_invariants.rst b/book/pulse/pulse_atomics_and_invariants.rst index f6cf48271d5..a9c86cd4311 100755 --- a/book/pulse/pulse_atomics_and_invariants.rst +++ b/book/pulse/pulse_atomics_and_invariants.rst @@ -357,7 +357,7 @@ and *later credits*, features found in Iris and other separation logics that feature impredicativity. The Later Modality and Later Credits -------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The predicate ``later p`` states that the ``p:slprop`` is true after one tick. @@ -417,7 +417,7 @@ Note, later credits can also be split and combined additively: : Lemma (later_credit (a + b) == later_credit a ** later_credit b) Timeless Predicates -------------------- +~~~~~~~~~~~~~~~~~~~ All predicates ``p:slprop`` are hereditary, meaning that ``p`` implies ``later p``. Some predicates, including many common predicates like ``pts_to`` are also From 6187fdd1e24ea0d328809a39cf85121cd8032c99 Mon Sep 17 00:00:00 2001 From: utgheith Date: Mon, 18 Aug 2025 08:44:38 -0500 Subject: [PATCH 58/65] Fixed typo ['i + i' --> 'i + 1'] --- book/part2/part2_universes.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/part2/part2_universes.rst b/book/part2/part2_universes.rst index 46ef177741c..145bd1e6501 100644 --- a/book/part2/part2_universes.rst +++ b/book/part2/part2_universes.rst @@ -555,7 +555,7 @@ Raising universes and the lack of cumulativity ---------------------------------------------- In some type theories, notably in Coq, the universe system is -*cumulative*, meaning that ``Type u#i : Type u#(max (i + i) j)``; +*cumulative*, meaning that ``Type u#i : Type u#(max (i + 1) j)``; or, that ``Type u#i`` inhabits all universes greater than ``i``. In contrast, in F*, as in Agda and Lean, ``Type u#i : Type u#(i + 1)``, i.e., a type resides only in the universe immediately @@ -757,3 +757,4 @@ detect and fix such problems. :language: fstar :start-after: //SNIPPET_START: crazy_index$ :end-before: //SNIPPET_END: crazy_index$ + From 966eca3036638b1960e35ab6704b7043526aafa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Fri, 14 Feb 2025 08:52:21 -0800 Subject: [PATCH 59/65] Update after FStarLang/FStar#3753 --- book/pulse/pulse_extraction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/pulse/pulse_extraction.rst b/book/pulse/pulse_extraction.rst index b943f8d1216..05d9006cb42 100644 --- a/book/pulse/pulse_extraction.rst +++ b/book/pulse/pulse_extraction.rst @@ -82,7 +82,7 @@ and then the second command extracts the Rust code to ``voting.rs``. (These comm $ fstar.exe --include lib/pulse/ --include lib/pulse/lib --include share/pulse/examples/by-example/ --include share/pulse/examples/_output/cache/ --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst - --extract 'PulseTutorial.Algorithms' --codegen Extension + --codegen Extension $ ./pulse2rust/main.exe PulseTutorial_Algorithms.ast -o voting.rs @@ -188,7 +188,7 @@ For the Boyer-Moore example, we can extract the program to OCaml as follows: $ fstar.exe --include lib/pulse/ --include lib/pulse/lib --include share/pulse/examples/by-example/ --include share/pulse/examples/_output/cache/ --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst - --extract 'PulseTutorial.Algorithms' --codegen OCaml + --codegen OCaml and the extracted ``majority`` function looks like: From 3a8ac3fb1bad3fc2160fd2c2d2715ecd6efab200 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Mon, 18 Aug 2025 08:20:41 -0700 Subject: [PATCH 60/65] tweak a la carte --- book/part3/part3_alacarte.rst | 40 +++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/book/part3/part3_alacarte.rst b/book/part3/part3_alacarte.rst index 7b15e432303..b350bb10f05 100644 --- a/book/part3/part3_alacarte.rst +++ b/book/part3/part3_alacarte.rst @@ -388,8 +388,9 @@ preserves its semantics. This is the one part of this development where the definition is not completely generic in the type of expression nodes. Instead, this is proof for the specific case of expressions that contain values, additions, and multiplications. I -haven't found a way to make this more generic. If you know a way, please let me -know! +haven't found a way to make this more generic. One would likely need to define a +generic induction principle similar in structure to ``fold_expr``---but that's +for another day's head scratching. If you know an easy way, please let me know! That said, the proof is quite straightforward and pleasant: We simply match on the cases, use the induction hypothesis on the subtrees if any, and then apply @@ -444,3 +445,38 @@ print any expression of type ``expr f``. :language: fstar :start-after: //SNIPPET_START: to_string$ :end-before: //SNIPPET_END: to_string$ + +Exercise 3 +++++++++++ + +Write a function ``lift`` with the following signature + +.. code-block:: fstar + + let lift #f #g + {| ff: functor f |} + {| fg: leq f g |} + (x: expr f) + : expr g + +Use it to reuse an expression defined for one type to another, so that the +assertion below success + +.. code-block:: fstar + + let ex3 : expr (value ++ add ++ mul) = lift addExample *^ v 2 + + [@@expect_failure] + let test_e3 = assert_norm (eval_expr ex3 == (1337 * 2)) + +.. container:: toggle + + .. container:: header + + **Answer** + + .. literalinclude:: ../code/Part3.DataTypesALaCarte.fst + :language: fstar + :start-after: //SNIPPET_START: lift$ + :end-before: //SNIPPET_END: lift$ + From cd175f835c1b7993da631619b6f8594a25b8aee9 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Mon, 18 Aug 2025 08:20:59 -0700 Subject: [PATCH 61/65] no need to explicitly sequence pulse commands --- book/pulse/pulse_ch2.rst | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index 83dc2c970db..886d81bd9d5 100644 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -198,31 +198,6 @@ The comments show how the proof state evolves after each command. * Pulse also uses the SMT solver to convert ``pts_to r (v2 + v2)`` to ``pts_to r (4 * 'v)``. -Stateful commands are explicitly sequenced -.......................................... - -Pulse expects the results of all stateful operations to be explicitly -``let``-bound. For example, the following code fails to type check: - -.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst - :language: pulse - :start-after: //quad FAIL$ - :end-before: //end quad FAIL$ - - -.. code-block:: - - - Expected type "int"; but "!r" has type - "stt int - (pts_to r (reveal (*?u93*) _)) - (fun x -> pts_to r x ** pure (reveal (*?u93*) _ == x))" - -The error points to the first occurrence of ``(!r)``. The message is -admittedly cryptic and should be improved. As we'll see in a later -chapter, the type ``stt _ _ _`` is the type of an unevaluated Pulse -computation---this error complains that ``add`` expected an argument -of type ``int`` but instead got an unevaluated computation. - Fractional Permissions ...................... From 103ab43a07691a3f3357b2a19bde614f33b3d4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Mon, 18 Aug 2025 08:33:37 -0700 Subject: [PATCH 62/65] pulse: bring in missing snippet This was in the website, but not here. --- book/pulse/pulse_ch2.rst | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index 886d81bd9d5..1f482b6af20 100644 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -198,6 +198,32 @@ The comments show how the proof state evolves after each command. * Pulse also uses the SMT solver to convert ``pts_to r (v2 + v2)`` to ``pts_to r (4 * 'v)``. + +Stateful commands are explicitly sequenced +.......................................... + +Pulse expects the results of all stateful operations to be explicitly +``let``-bound. For example, the following code fails to type check: + +.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst + :language: pulse + :start-after: //quad FAIL$ + :end-before: //end quad FAIL$ + + +.. code-block:: + + - Expected type "int"; but "!r" has type + "stt int + (pts_to r (reveal (*?u93*) _)) + (fun x -> pts_to r x ** pure (reveal (*?u93*) _ == x))" + +The error points to the first occurrence of ``(!r)``. The message is +admittedly cryptic and should be improved. As we'll see in a later +chapter, the type ``stt _ _ _`` is the type of an unevaluated Pulse +computation---this error complains that ``add`` expected an argument +of type ``int`` but instead got an unevaluated computation. + Fractional Permissions ...................... From 31109fa58ad432b243770308459ae86aa66dd557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Wed, 19 Nov 2025 22:37:48 -0800 Subject: [PATCH 63/65] Update Pulse extraction commands, needs --cmi --- book/pulse/pulse_extraction.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/book/pulse/pulse_extraction.rst b/book/pulse/pulse_extraction.rst index 05d9006cb42..469dd18e4d9 100644 --- a/book/pulse/pulse_extraction.rst +++ b/book/pulse/pulse_extraction.rst @@ -79,9 +79,9 @@ and then the second command extracts the Rust code to ``voting.rs``. (These comm .. code-block:: shell - $ fstar.exe --include lib/pulse/ --include lib/pulse/lib - --include share/pulse/examples/by-example/ --include share/pulse/examples/_output/cache/ - --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst + $ fstar.exe --include out/lib/pulse/ + --include share/pulse/examples/by-example/ --include share/pulse/examples/_cache/ + --cmi --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst --codegen Extension $ ./pulse2rust/main.exe PulseTutorial_Algorithms.ast -o voting.rs @@ -141,9 +141,9 @@ Then we extract it to C as follows (the commands are run in the ``pulse`` root d .. code-block:: shell - $ fstar.exe --include lib/pulse/ --include lib/pulse/lib - --include share/pulse/examples/by-example/ --include share/pulse/examples/_output/cache/ - --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst + $ fstar.exe --include out/lib/pulse/ + --include share/pulse/examples/by-example/ --include share/pulse/examples/_cache/ + --cmi --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst --extract 'FStar.Pervasives.Native PulseTutorial.Algorithms' --codegen krml $ ../karamel/krml -skip-compilation out.krml @@ -185,9 +185,9 @@ For the Boyer-Moore example, we can extract the program to OCaml as follows: .. code-block:: shell - $ fstar.exe --include lib/pulse/ --include lib/pulse/lib - --include share/pulse/examples/by-example/ --include share/pulse/examples/_output/cache/ - --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst + $ fstar.exe --include out/lib/pulse/ + --include share/pulse/examples/by-example/ --include share/pulse/examples/_cache/ + --cmi --load_cmxs pulse --odir . PulseTutorial.Algorithms.fst --codegen OCaml and the extracted ``majority`` function looks like: From cd2974840ca7edc4e9c5ce9dac9286628162aa14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Mart=C3=ADnez?= Date: Wed, 19 Nov 2025 22:41:51 -0800 Subject: [PATCH 64/65] Pulse: remove section about needing to let-bind, this is gone --- book/pulse/pulse_ch2.rst | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/book/pulse/pulse_ch2.rst b/book/pulse/pulse_ch2.rst index 1f482b6af20..886d81bd9d5 100644 --- a/book/pulse/pulse_ch2.rst +++ b/book/pulse/pulse_ch2.rst @@ -198,32 +198,6 @@ The comments show how the proof state evolves after each command. * Pulse also uses the SMT solver to convert ``pts_to r (v2 + v2)`` to ``pts_to r (4 * 'v)``. - -Stateful commands are explicitly sequenced -.......................................... - -Pulse expects the results of all stateful operations to be explicitly -``let``-bound. For example, the following code fails to type check: - -.. literalinclude:: ../code/pulse/PulseTutorial.Ref.fst - :language: pulse - :start-after: //quad FAIL$ - :end-before: //end quad FAIL$ - - -.. code-block:: - - - Expected type "int"; but "!r" has type - "stt int - (pts_to r (reveal (*?u93*) _)) - (fun x -> pts_to r x ** pure (reveal (*?u93*) _ == x))" - -The error points to the first occurrence of ``(!r)``. The message is -admittedly cryptic and should be improved. As we'll see in a later -chapter, the type ``stt _ _ _`` is the type of an unevaluated Pulse -computation---this error complains that ``add`` expected an argument -of type ``int`` but instead got an unevaluated computation. - Fractional Permissions ...................... From d0e421a00821230272bb87b15e3863980555ce31 Mon Sep 17 00:00:00 2001 From: Nikhil Swamy Date: Wed, 1 Apr 2026 23:30:12 -0700 Subject: [PATCH 65/65] Adjust PoP-in-FStar Makefile for new location in FStar repo Update FSTAR_HOME/PULSE_HOME paths and use symlinks instead of copies in prep target since the book now lives in the same repo. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- doc/book/PoP-in-FStar/book/Makefile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/book/PoP-in-FStar/book/Makefile b/doc/book/PoP-in-FStar/book/Makefile index 7c46bcaa4f3..e60ff8a33c8 100644 --- a/doc/book/PoP-in-FStar/book/Makefile +++ b/doc/book/PoP-in-FStar/book/Makefile @@ -1,8 +1,9 @@ # Minimal makefile for Sphinx documentation # -export FSTAR_HOME?=$(realpath ../../everest/FStar) -export PULSE_HOME?=$(FSTAR_HOME)/../pulse +# This book lives at doc/book/PoP-in-FStar/book/ within the FStar repo +export FSTAR_HOME?=$(realpath ../../../..) +export PULSE_HOME?=$(FSTAR_HOME)/pulse FSTARLANG_ORG_ROOT?=www @@ -14,9 +15,9 @@ SOURCEDIR = . BUILDDIR = _build prep: - mkdir -p code/pulse - cp -R $(FSTAR_HOME)/doc/book/code/* code - cp -R $(PULSE_HOME)/share/pulse/examples/by-example/* code/pulse + mkdir -p code + ln -sfn $(realpath $(FSTAR_HOME)/doc/book/code)/* code/ + ln -sfn $(realpath $(PULSE_HOME)/share/pulse/examples/by-example) code/pulse html: Makefile