diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000000..f17311098f54 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ +This file makes sure that Github Pages doesn't process mdBook's output. diff --git a/404.html b/404.html new file mode 100644 index 000000000000..b233fd9fa8bd --- /dev/null +++ b/404.html @@ -0,0 +1,170 @@ + + + + + + Page not found - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Document not found (404)

+

This URL is invalid, sorry. Please use the navigation bar or search to continue.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/FontAwesome/css/font-awesome.css b/FontAwesome/css/font-awesome.css new file mode 100644 index 000000000000..540440ce89f2 --- /dev/null +++ b/FontAwesome/css/font-awesome.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/FontAwesome/fonts/FontAwesome.ttf b/FontAwesome/fonts/FontAwesome.ttf new file mode 100644 index 000000000000..35acda2fa119 Binary files /dev/null and b/FontAwesome/fonts/FontAwesome.ttf differ diff --git a/FontAwesome/fonts/fontawesome-webfont.eot b/FontAwesome/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000000..e9f60ca953f9 Binary files /dev/null and b/FontAwesome/fonts/fontawesome-webfont.eot differ diff --git a/FontAwesome/fonts/fontawesome-webfont.svg b/FontAwesome/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000000..855c845e538b --- /dev/null +++ b/FontAwesome/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FontAwesome/fonts/fontawesome-webfont.ttf b/FontAwesome/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000000..35acda2fa119 Binary files /dev/null and b/FontAwesome/fonts/fontawesome-webfont.ttf differ diff --git a/FontAwesome/fonts/fontawesome-webfont.woff b/FontAwesome/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000000..400014a4b06e Binary files /dev/null and b/FontAwesome/fonts/fontawesome-webfont.woff differ diff --git a/FontAwesome/fonts/fontawesome-webfont.woff2 b/FontAwesome/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000000..4d13fc60404b Binary files /dev/null and b/FontAwesome/fonts/fontawesome-webfont.woff2 differ diff --git a/application.html b/application.html new file mode 100644 index 000000000000..7fd6795c63fe --- /dev/null +++ b/application.html @@ -0,0 +1,195 @@ + + + + + + Application - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Application

+

You may be interested in applying Kani if you're in this situation:

+
    +
  1. You're working on a moderately important project in Rust.
  2. +
  3. You've already invested heavily in testing to ensure correctness.
  4. +
  5. You want to invest further, to gain a much higher degree of assurance.
  6. +
+
+

If you haven't already, we also recommend techniques like property testing and fuzzing (e.g. with bolero). +These yield good results, are very cheap to apply, and are often easy to adopt and debug.

+
+

In this section, we explain how Kani compares with other tools +and suggest where to start applying Kani in real code.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/ayu-highlight.css b/ayu-highlight.css new file mode 100644 index 000000000000..0c45c6f14608 --- /dev/null +++ b/ayu-highlight.css @@ -0,0 +1,79 @@ +/* +Based off of the Ayu theme +Original by Dempfi (https://github.com/dempfi/ayu) +*/ + +.hljs { + display: block; + overflow-x: auto; + background: #191f26; + color: #e6e1cf; + padding: 0.5em; +} + +.hljs-comment, +.hljs-quote { + color: #5c6773; + font-style: italic; +} + +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-attr, +.hljs-regexp, +.hljs-link, +.hljs-selector-id, +.hljs-selector-class { + color: #ff7733; +} + +.hljs-number, +.hljs-meta, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #ffee99; +} + +.hljs-string, +.hljs-bullet { + color: #b8cc52; +} + +.hljs-title, +.hljs-built_in, +.hljs-section { + color: #ffb454; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-symbol { + color: #ff7733; +} + +.hljs-name { + color: #36a3d9; +} + +.hljs-tag { + color: #00568d; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #91b362; +} + +.hljs-deletion { + color: #d96c75; +} diff --git a/benchcomp-cli.html b/benchcomp-cli.html new file mode 100644 index 000000000000..45fbb85eb1ba --- /dev/null +++ b/benchcomp-cli.html @@ -0,0 +1,260 @@ + + + + + + benchcomp command line - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

benchcomp command line

+

benchcomp is a single command that runs benchmarks, parses their results, combines these results, and emits visualizations. +benchcomp also provides subcommands to run these steps individually. +Most users will want to invoke benchcomp in one of two ways:

+
    +
  • benchcomp without any subcommands, which runs the entire performance comparison process as depicted below
  • +
  • benchcomp visualize, which runs the visualization step on the results of a previous benchmark run without actually re-running the benchmarks. +This is useful when tweaking the parameters of a visualization, for example changing the threshold of what is considered to be a regression.
  • +
+

The subcommands run and collate are also available. +The diagram below depicts benchcomp's order of operation.

Gcluster_runbenchcomp runcluster_collatebenchcomp collatecluster_vizualizebenchcomp visualizesuite_1asuite_1out_1aoutputfilessuite_1a->out_1arun withvariant asuite_1a_yamlsuite_1a.yamlout_1a->suite_1a_yamlsuite_1_parser.pyresult_yamlresult.yamlsuite_1a_yaml->result_yamlsuite_1bsuite_1out_1boutputfilessuite_1b->out_1brun withvariant bsuite_1b_yamlsuite_1b.yamlout_1b->suite_1b_yamlsuite_1_parser.pysuite_1b_yaml->result_yamlsuite_2csuite_2out_2coutputfilessuite_2c->out_2crun withvariant asuite_2c_yamlsuite_2c.yamlout_2c->suite_2c_yamlsuite_2_parser.pysuite_2c_yaml->result_yamlsuite_2dsuite_2out_2doutputfilessuite_2d->out_2drun withvariant bsuite_2d_yamlsuite_2d.yamlout_2d->suite_2d_yamlsuite_2_parser.pysuite_2d_yaml->result_yamlviz_1graph.svgresult_yaml->viz_1viz_2summary.mdresult_yaml->viz_2viz_3exit 1 onregressionresult_yaml->viz_3

+

Running benchcomp invokes run, collate, and visualize behind the scenes. +If you have previously run benchcomp, then running benchcomp visualize will emit the visualizations in the config file using the previous result.yaml.

+

In the diagram above, two different suites (1 and 2) are both run using two variants---combinations of command, working directory, and environment variables. +Benchmark suite 2 requires a totally different command line to suite 1---for example, suite_1 might contain Kani harnesses invoked through cargo kani, while suite_2 might contain CBMC harnesses invoked through run_cbmc_proofs.py. +Users would therefore define different variants (c and d) for invoking suite_2, and also specify a different parser to parse the results. +No matter how different the benchmark suites are, the collate stage combines their results so that they can later be compared.

+

Example config file

+

Users must specify the actual suites to run, the parsers used to collect their results, and the visualizations to emit in a file called benchcomp.yaml or a file passed to the -c/--config flag. +The next section describes the schema for this configuration file. +A run similar to the diagram above might be achieved using the following configuration file:

+
# Compare a range of Kani and CBMC benchmarks when
+# using Cadical versus the default SAT solver
+
+variants:
+  variant_a:
+    config:
+      directory: kani_benchmarks
+      command_line: scripts/kani-perf.sh
+      env: {}
+
+  variant_b:
+    config:
+      directory: kani_benchmarks
+      command_line: scripts/kani-perf.sh
+      # This variant uses a hypothetical environment variable that
+      # forces Kani to use the cadical SAT solver
+      env:
+        KANI_SOLVER: cadical
+
+  variant_c:
+    config:
+      directory: cbmc_benchmarks
+      command_line: run_cbmc_proofs.py
+      env: {}
+
+  variant_d:
+    config:
+      directory: cbmc_benchmarks
+      command_line: run_cbmc_proofs.py
+      env:
+        EXTERNAL_SAT_SOLVER: cadical
+
+run:
+  suites:
+    suite_1:
+      parser:
+        module: kani_perf
+      variants: [variant_a, variant_b]
+
+    suite_2:
+      parser:
+        module: cbmc_litani_parser
+      variants: [variant_c, variant_d]
+
+visualize:
+  - type: dump_graph
+    out_file: graph.svg
+
+  - type: dump_markdown_results_table
+    out_file: summary.md
+    extra_columns: []
+
+  - type: error_on_regression
+    variant_pairs:
+    - [variant_a, variant_b]
+    - [variant_c, variant_d]
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/benchcomp-conf.html b/benchcomp-conf.html new file mode 100644 index 000000000000..f8e8fef54114 --- /dev/null +++ b/benchcomp-conf.html @@ -0,0 +1,319 @@ + + + + + + benchcomp configuration file - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

benchcomp configuration file

+

benchcomp's operation is controlled through a YAML file---benchcomp.yaml by default or a file passed to the -c/--config option. +This page lists the different visualizations that are available.

+

Variants

+

A variant is a single invocation of a benchmark suite. Benchcomp runs several +variants, so that their performance can be compared later. A variant consists of +a command-line argument, working directory, and environment. Benchcomp invokes +the command using the operating system environment, updated with the keys and +values in env. If any values in env contain strings of the form ${var}, +Benchcomp expands them to the value of the environment variable $var.

+
variants:
+    variant_1:
+        config:
+            command_line: echo "Hello, world"
+            directory: /tmp
+            env:
+              PATH: /my/local/directory:${PATH}
+
+

Filters

+

After benchcomp has finished parsing the results, it writes the results to results.yaml by default. +Before visualizing the results (see below), benchcomp can filter the results by piping them into an external program.

+

To filter results before visualizing them, add filters to the configuration file.

+
filters:
+    - command_line: ./scripts/remove-redundant-results.py
+    - command_line: cat
+
+

The value of filters is a list of dicts. +Currently the only legal key for each of the dicts is command_line. +Benchcomp invokes each command_line in order, passing the results as a JSON file on stdin, and interprets the stdout as a YAML-formatted modified set of results. +Filter scripts can emit either YAML (which might be more readable while developing the script), or JSON (which benchcomp will parse as a subset of YAML).

+

Built-in visualizations

+

The following visualizations are available; these can be added to the visualize list of benchcomp.yaml.

+ +

Detailed documentation for these visualizations follows.

+

Plot

+

Scatterplot configuration options

+

dump_markdown_results_table

+

Print Markdown-formatted tables displaying benchmark results

+

For each metric, this visualization prints out a table of benchmarks, +showing the value of the metric for each variant, combined with an optional +scatterplot.

+

The 'out_file' key is mandatory; specify '-' to print to stdout.

+

'extra_colums' can be an empty dict. The sample configuration below assumes +that each benchmark result has a 'success' and 'runtime' metric for both +variants, 'variant_1' and 'variant_2'. It adds a 'ratio' column to the table +for the 'runtime' metric, and a 'change' column to the table for the +'success' metric. The 'text' lambda is called once for each benchmark. The +'text' lambda accepts a single argument---a dict---that maps variant +names to the value of that variant for a particular metric. The lambda +returns a string that is rendered in the benchmark's row in the new column. +This allows you to emit arbitrary text or markdown formatting in response to +particular combinations of values for different variants, such as +regressions or performance improvements.

+

'scatterplot' takes the values 'off' (default), 'linear' (linearly scaled +axes), or 'log' (logarithmically scaled axes).

+

Sample configuration:

+
visualize:
+- type: dump_markdown_results_table
+  out_file: "-"
+  scatterplot: linear
+  extra_columns:
+    runtime:
+    - column_name: ratio
+      text: >
+        lambda b: str(b["variant_2"]/b["variant_1"])
+        if b["variant_2"] < (1.5 * b["variant_1"])
+        else "**" + str(b["variant_2"]/b["variant_1"]) + "**"
+    success:
+    - column_name: change
+      text: >
+        lambda b: "" if b["variant_2"] == b["variant_1"]
+        else "newly passing" if b["variant_2"]
+        else "regressed"
+
+

Example output:

+
## runtime
+
+| Benchmark |  variant_1 | variant_2 | ratio |
+| --- | --- | --- | --- |
+| bench_1 | 5 | 10 | **2.0** |
+| bench_2 | 10 | 5 | 0.5 |
+
+## success
+
+| Benchmark |  variant_1 | variant_2 | change |
+| --- | --- | --- | --- |
+| bench_1 | True | True |  |
+| bench_2 | True | False | regressed |
+| bench_3 | False | True | newly passing |
+
+

dump_yaml

+

Print the YAML-formatted results to a file.

+

The 'out_file' key is mandatory; specify '-' to print to stdout.

+

Sample configuration:

+
visualize:
+- type: dump_yaml
+  out_file: '-'
+
+

error_on_regression

+

Terminate benchcomp with a return code of 1 if any benchmark regressed.

+

This visualization checks whether any benchmark regressed from one variant +to another. Sample configuration:

+
visualize:
+- type: error_on_regression
+  variant_pairs:
+  - [variant_1, variant_2]
+  - [variant_1, variant_3]
+  checks:
+  - metric: runtime
+    test: "lambda old, new: new / old > 1.1"
+  - metric: passed
+    test: "lambda old, new: False if not old else not new"
+
+

This says to check whether any benchmark regressed when run under variant_2 +compared to variant_1. A benchmark is considered to have regressed if the +value of the 'runtime' metric under variant_2 is 10% higher than the value +under variant_1. Furthermore, the benchmark is also considered to have +regressed if it was previously passing, but is now failing. These same +checks are performed on all benchmarks run under variant_3 compared to +variant_1. If any of those lambda functions returns True, then benchcomp +will terminate with a return code of 1.

+

run_command

+

Run an executable command, passing the performance metrics as JSON on stdin.

+

This allows you to write your own visualization, which reads a result file +on stdin and does something with it, e.g. writing out a graph or other +output file.

+

Sample configuration:

+
visualize:
+- type: run_command
+  command: ./my_visualization.py
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/benchcomp-parse.html b/benchcomp-parse.html new file mode 100644 index 000000000000..0446ec6ee819 --- /dev/null +++ b/benchcomp-parse.html @@ -0,0 +1,227 @@ + + + + + + Custom parsers - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Custom parsers

+

Benchcomp ships with built-in parsers that retrieve the results of a benchmark suite after the run has completed. +You can also create your own parser, either to run locally or to check into the Kani codebase.

+

Built-in parsers

+

You specify which parser should run for each benchmark suite in benchcomp.yaml. +For example, if you're running the kani performance suite, you would use the built-in kani_perf parser to parse the results:

+
suites:
+    my_benchmark_suite:
+      variants: [variant_1, variant_2]
+      parser:
+        module: kani_perf
+
+

Custom parsers

+

A parser is a program that benchcomp runs inside the root directory of a benchmark suite, after the suite run has completed. +The parser should retrieve the results of the run (by parsing output files etc.) and print the results out as a YAML document. +You can use your executable parser by specifying the command key rather than the module key in your benchconf.yaml file:

+
suites:
+    my_benchmark_suite:
+      variants: [variant_1, variant_2]
+      parser:
+        command: ./my-cool-parser.sh
+
+

The kani_perf parser mentioned above, in tools/benchcomp/benchcomp/parsers/kani_perf.py, is a good starting point for writing a custom parser, as it also works as a standalone executable. +Here is an example output from an executable parser:

+
metrics:
+    runtime: {}
+    success: {}
+    errors: {}
+benchmarks:
+    bench_1:
+        metrics:
+            runtime: 32
+            success: true
+            errors: []
+    bench_2:
+        metrics:
+            runtime: 0
+            success: false
+            errors: ["compilation failed"]
+
+

The above format is different from the final result.yaml file that benchcomp writes, because the above file represents the output of running a single benchmark suite using a single variant. +Your parser will run once for each variant, and benchcomp combines the dictionaries into the final result.yaml file.

+

Contributing custom parsers to Kani

+

To turn your executable parser into one that benchcomp can invoke as a module, ensure that it has a main(working_directory) method that returns a dict (the same dict that it would print out as a YAML file to stdout). +Save the file in tools/benchcomp/benchcomp/parsers using python module naming conventions (filename should be an identifier and end in .py).

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/book.js b/book.js new file mode 100644 index 000000000000..d40440c72317 --- /dev/null +++ b/book.js @@ -0,0 +1,679 @@ +"use strict"; + +// Fix back button cache problem +window.onunload = function () { }; + +// Global variable, shared between modules +function playground_text(playground) { + let code_block = playground.querySelector("code"); + + if (window.ace && code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + return editor.getValue(); + } else { + return code_block.textContent; + } +} + +(function codeSnippets() { + function fetch_with_timeout(url, options, timeout = 6000) { + return Promise.race([ + fetch(url, options), + new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)) + ]); + } + + var playgrounds = Array.from(document.querySelectorAll(".playground")); + if (playgrounds.length > 0) { + fetch_with_timeout("https://play.rust-lang.org/meta/crates", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + }) + .then(response => response.json()) + .then(response => { + // get list of crates available in the rust playground + let playground_crates = response.crates.map(item => item["id"]); + playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); + }); + } + + function handle_crate_list_update(playground_block, playground_crates) { + // update the play buttons after receiving the response + update_play_button(playground_block, playground_crates); + + // and install on change listener to dynamically update ACE editors + if (window.ace) { + let code_block = playground_block.querySelector("code"); + if (code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + editor.addEventListener("change", function (e) { + update_play_button(playground_block, playground_crates); + }); + // add Ctrl-Enter command to execute rust code + editor.commands.addCommand({ + name: "run", + bindKey: { + win: "Ctrl-Enter", + mac: "Ctrl-Enter" + }, + exec: _editor => run_rust_code(playground_block) + }); + } + } + } + + // updates the visibility of play button based on `no_run` class and + // used crates vs ones available on http://play.rust-lang.org + function update_play_button(pre_block, playground_crates) { + var play_button = pre_block.querySelector(".play-button"); + + // skip if code is `no_run` + if (pre_block.querySelector('code').classList.contains("no_run")) { + play_button.classList.add("hidden"); + return; + } + + // get list of `extern crate`'s from snippet + var txt = playground_text(pre_block); + var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g; + var snippet_crates = []; + var item; + while (item = re.exec(txt)) { + snippet_crates.push(item[1]); + } + + // check if all used crates are available on play.rust-lang.org + var all_available = snippet_crates.every(function (elem) { + return playground_crates.indexOf(elem) > -1; + }); + + if (all_available) { + play_button.classList.remove("hidden"); + } else { + play_button.classList.add("hidden"); + } + } + + function run_rust_code(code_block) { + var result_block = code_block.querySelector(".result"); + if (!result_block) { + result_block = document.createElement('code'); + result_block.className = 'result hljs language-bash'; + + code_block.append(result_block); + } + + let text = playground_text(code_block); + let classes = code_block.querySelector('code').classList; + let edition = "2015"; + if(classes.contains("edition2018")) { + edition = "2018"; + } else if(classes.contains("edition2021")) { + edition = "2021"; + } + var params = { + version: "stable", + optimize: "0", + code: text, + edition: edition + }; + + if (text.indexOf("#![feature") !== -1) { + params.version = "nightly"; + } + + result_block.innerText = "Running..."; + + fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params) + }) + .then(response => response.json()) + .then(response => { + if (response.result.trim() === '') { + result_block.innerText = "No output"; + result_block.classList.add("result-no-output"); + } else { + result_block.innerText = response.result; + result_block.classList.remove("result-no-output"); + } + }) + .catch(error => result_block.innerText = "Playground Communication: " + error.message); + } + + // Syntax highlighting Configuration + hljs.configure({ + tabReplace: ' ', // 4 spaces + languages: [], // Languages used for auto-detection + }); + + let code_nodes = Array + .from(document.querySelectorAll('code')) + // Don't highlight `inline code` blocks in headers. + .filter(function (node) {return !node.parentElement.classList.contains("header"); }); + + if (window.ace) { + // language-rust class needs to be removed for editable + // blocks or highlightjs will capture events + code_nodes + .filter(function (node) {return node.classList.contains("editable"); }) + .forEach(function (block) { block.classList.remove('language-rust'); }); + + Array + code_nodes + .filter(function (node) {return !node.classList.contains("editable"); }) + .forEach(function (block) { hljs.highlightBlock(block); }); + } else { + code_nodes.forEach(function (block) { hljs.highlightBlock(block); }); + } + + // Adding the hljs class gives code blocks the color css + // even if highlighting doesn't apply + code_nodes.forEach(function (block) { block.classList.add('hljs'); }); + + Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { + + var lines = Array.from(block.querySelectorAll('.boring')); + // If no lines were hidden, return + if (!lines.length) { return; } + block.classList.add("hide-boring"); + + var buttons = document.createElement('div'); + buttons.className = 'buttons'; + buttons.innerHTML = ""; + + // add expand button + var pre_block = block.parentNode; + pre_block.insertBefore(buttons, pre_block.firstChild); + + pre_block.querySelector('.buttons').addEventListener('click', function (e) { + if (e.target.classList.contains('fa-eye')) { + e.target.classList.remove('fa-eye'); + e.target.classList.add('fa-eye-slash'); + e.target.title = 'Hide lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.remove('hide-boring'); + } else if (e.target.classList.contains('fa-eye-slash')) { + e.target.classList.remove('fa-eye-slash'); + e.target.classList.add('fa-eye'); + e.target.title = 'Show hidden lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.add('hide-boring'); + } + }); + }); + + if (window.playground_copyable) { + Array.from(document.querySelectorAll('pre code')).forEach(function (block) { + var pre_block = block.parentNode; + if (!pre_block.classList.contains('playground')) { + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var clipButton = document.createElement('button'); + clipButton.className = 'fa fa-copy clip-button'; + clipButton.title = 'Copy to clipboard'; + clipButton.setAttribute('aria-label', clipButton.title); + clipButton.innerHTML = ''; + + buttons.insertBefore(clipButton, buttons.firstChild); + } + }); + } + + // Process playground code blocks + Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) { + // Add play button + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var runCodeButton = document.createElement('button'); + runCodeButton.className = 'fa fa-play play-button'; + runCodeButton.hidden = true; + runCodeButton.title = 'Run this code'; + runCodeButton.setAttribute('aria-label', runCodeButton.title); + + buttons.insertBefore(runCodeButton, buttons.firstChild); + runCodeButton.addEventListener('click', function (e) { + run_rust_code(pre_block); + }); + + if (window.playground_copyable) { + var copyCodeClipboardButton = document.createElement('button'); + copyCodeClipboardButton.className = 'fa fa-copy clip-button'; + copyCodeClipboardButton.innerHTML = ''; + copyCodeClipboardButton.title = 'Copy to clipboard'; + copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title); + + buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild); + } + + let code_block = pre_block.querySelector("code"); + if (window.ace && code_block.classList.contains("editable")) { + var undoChangesButton = document.createElement('button'); + undoChangesButton.className = 'fa fa-history reset-button'; + undoChangesButton.title = 'Undo changes'; + undoChangesButton.setAttribute('aria-label', undoChangesButton.title); + + buttons.insertBefore(undoChangesButton, buttons.firstChild); + + undoChangesButton.addEventListener('click', function () { + let editor = window.ace.edit(code_block); + editor.setValue(editor.originalCode); + editor.clearSelection(); + }); + } + }); +})(); + +(function themes() { + var html = document.querySelector('html'); + var themeToggleButton = document.getElementById('theme-toggle'); + var themePopup = document.getElementById('theme-list'); + var themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); + var stylesheets = { + ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"), + tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"), + highlight: document.querySelector("[href$='highlight.css']"), + }; + + function showThemes() { + themePopup.style.display = 'block'; + themeToggleButton.setAttribute('aria-expanded', true); + themePopup.querySelector("button#" + get_theme()).focus(); + } + + function hideThemes() { + themePopup.style.display = 'none'; + themeToggleButton.setAttribute('aria-expanded', false); + themeToggleButton.focus(); + } + + function get_theme() { + var theme; + try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { } + if (theme === null || theme === undefined) { + return default_theme; + } else { + return theme; + } + } + + function set_theme(theme, store = true) { + let ace_theme; + + if (theme == 'coal' || theme == 'navy') { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = false; + stylesheets.highlight.disabled = true; + + ace_theme = "ace/theme/tomorrow_night"; + } else if (theme == 'ayu') { + stylesheets.ayuHighlight.disabled = false; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = true; + ace_theme = "ace/theme/tomorrow_night"; + } else { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = false; + ace_theme = "ace/theme/dawn"; + } + + setTimeout(function () { + themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor; + }, 1); + + if (window.ace && window.editors) { + window.editors.forEach(function (editor) { + editor.setTheme(ace_theme); + }); + } + + var previousTheme = get_theme(); + + if (store) { + try { localStorage.setItem('mdbook-theme', theme); } catch (e) { } + } + + html.classList.remove(previousTheme); + html.classList.add(theme); + } + + // Set theme + var theme = get_theme(); + + set_theme(theme, false); + + themeToggleButton.addEventListener('click', function () { + if (themePopup.style.display === 'block') { + hideThemes(); + } else { + showThemes(); + } + }); + + themePopup.addEventListener('click', function (e) { + var theme; + if (e.target.className === "theme") { + theme = e.target.id; + } else if (e.target.parentElement.className === "theme") { + theme = e.target.parentElement.id; + } else { + return; + } + set_theme(theme); + }); + + themePopup.addEventListener('focusout', function(e) { + // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) + if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) { + hideThemes(); + } + }); + + // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 + document.addEventListener('click', function(e) { + if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { + hideThemes(); + } + }); + + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (!themePopup.contains(e.target)) { return; } + + switch (e.key) { + case 'Escape': + e.preventDefault(); + hideThemes(); + break; + case 'ArrowUp': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.previousElementSibling) { + li.previousElementSibling.querySelector('button').focus(); + } + break; + case 'ArrowDown': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.nextElementSibling) { + li.nextElementSibling.querySelector('button').focus(); + } + break; + case 'Home': + e.preventDefault(); + themePopup.querySelector('li:first-child button').focus(); + break; + case 'End': + e.preventDefault(); + themePopup.querySelector('li:last-child button').focus(); + break; + } + }); +})(); + +(function sidebar() { + var html = document.querySelector("html"); + var sidebar = document.getElementById("sidebar"); + var sidebarLinks = document.querySelectorAll('#sidebar a'); + var sidebarToggleButton = document.getElementById("sidebar-toggle"); + var sidebarResizeHandle = document.getElementById("sidebar-resize-handle"); + var firstContact = null; + + function showSidebar() { + html.classList.remove('sidebar-hidden') + html.classList.add('sidebar-visible'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', 0); + }); + sidebarToggleButton.setAttribute('aria-expanded', true); + sidebar.setAttribute('aria-hidden', false); + try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { } + } + + + var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle'); + + function toggleSection(ev) { + ev.currentTarget.parentElement.classList.toggle('expanded'); + } + + Array.from(sidebarAnchorToggles).forEach(function (el) { + el.addEventListener('click', toggleSection); + }); + + function hideSidebar() { + html.classList.remove('sidebar-visible') + html.classList.add('sidebar-hidden'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', -1); + }); + sidebarToggleButton.setAttribute('aria-expanded', false); + sidebar.setAttribute('aria-hidden', true); + try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { } + } + + // Toggle sidebar + sidebarToggleButton.addEventListener('click', function sidebarToggle() { + if (html.classList.contains("sidebar-hidden")) { + var current_width = parseInt( + document.documentElement.style.getPropertyValue('--sidebar-width'), 10); + if (current_width < 150) { + document.documentElement.style.setProperty('--sidebar-width', '150px'); + } + showSidebar(); + } else if (html.classList.contains("sidebar-visible")) { + hideSidebar(); + } else { + if (getComputedStyle(sidebar)['transform'] === 'none') { + hideSidebar(); + } else { + showSidebar(); + } + } + }); + + sidebarResizeHandle.addEventListener('mousedown', initResize, false); + + function initResize(e) { + window.addEventListener('mousemove', resize, false); + window.addEventListener('mouseup', stopResize, false); + html.classList.add('sidebar-resizing'); + } + function resize(e) { + var pos = (e.clientX - sidebar.offsetLeft); + if (pos < 20) { + hideSidebar(); + } else { + if (html.classList.contains("sidebar-hidden")) { + showSidebar(); + } + pos = Math.min(pos, window.innerWidth - 100); + document.documentElement.style.setProperty('--sidebar-width', pos + 'px'); + } + } + //on mouseup remove windows functions mousemove & mouseup + function stopResize(e) { + html.classList.remove('sidebar-resizing'); + window.removeEventListener('mousemove', resize, false); + window.removeEventListener('mouseup', stopResize, false); + } + + document.addEventListener('touchstart', function (e) { + firstContact = { + x: e.touches[0].clientX, + time: Date.now() + }; + }, { passive: true }); + + document.addEventListener('touchmove', function (e) { + if (!firstContact) + return; + + var curX = e.touches[0].clientX; + var xDiff = curX - firstContact.x, + tDiff = Date.now() - firstContact.time; + + if (tDiff < 250 && Math.abs(xDiff) >= 150) { + if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) + showSidebar(); + else if (xDiff < 0 && curX < 300) + hideSidebar(); + + firstContact = null; + } + }, { passive: true }); + + // Scroll sidebar to current active section + var activeSection = document.getElementById("sidebar").querySelector(".active"); + if (activeSection) { + // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView + activeSection.scrollIntoView({ block: 'center' }); + } +})(); + +(function chapterNavigation() { + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (window.search && window.search.hasFocus()) { return; } + + switch (e.key) { + case 'ArrowRight': + e.preventDefault(); + var nextButton = document.querySelector('.nav-chapters.next'); + if (nextButton) { + window.location.href = nextButton.href; + } + break; + case 'ArrowLeft': + e.preventDefault(); + var previousButton = document.querySelector('.nav-chapters.previous'); + if (previousButton) { + window.location.href = previousButton.href; + } + break; + } + }); +})(); + +(function clipboard() { + var clipButtons = document.querySelectorAll('.clip-button'); + + function hideTooltip(elem) { + elem.firstChild.innerText = ""; + elem.className = 'fa fa-copy clip-button'; + } + + function showTooltip(elem, msg) { + elem.firstChild.innerText = msg; + elem.className = 'fa fa-copy tooltipped'; + } + + var clipboardSnippets = new ClipboardJS('.clip-button', { + text: function (trigger) { + hideTooltip(trigger); + let playground = trigger.closest("pre"); + return playground_text(playground); + } + }); + + Array.from(clipButtons).forEach(function (clipButton) { + clipButton.addEventListener('mouseout', function (e) { + hideTooltip(e.currentTarget); + }); + }); + + clipboardSnippets.on('success', function (e) { + e.clearSelection(); + showTooltip(e.trigger, "Copied!"); + }); + + clipboardSnippets.on('error', function (e) { + showTooltip(e.trigger, "Clipboard error!"); + }); +})(); + +(function scrollToTop () { + var menuTitle = document.querySelector('.menu-title'); + + menuTitle.addEventListener('click', function () { + document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); + }); +})(); + +(function controllMenu() { + var menu = document.getElementById('menu-bar'); + + (function controllPosition() { + var scrollTop = document.scrollingElement.scrollTop; + var prevScrollTop = scrollTop; + var minMenuY = -menu.clientHeight - 50; + // When the script loads, the page can be at any scroll (e.g. if you reforesh it). + menu.style.top = scrollTop + 'px'; + // Same as parseInt(menu.style.top.slice(0, -2), but faster + var topCache = menu.style.top.slice(0, -2); + menu.classList.remove('sticky'); + var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster + document.addEventListener('scroll', function () { + scrollTop = Math.max(document.scrollingElement.scrollTop, 0); + // `null` means that it doesn't need to be updated + var nextSticky = null; + var nextTop = null; + var scrollDown = scrollTop > prevScrollTop; + var menuPosAbsoluteY = topCache - scrollTop; + if (scrollDown) { + nextSticky = false; + if (menuPosAbsoluteY > 0) { + nextTop = prevScrollTop; + } + } else { + if (menuPosAbsoluteY > 0) { + nextSticky = true; + } else if (menuPosAbsoluteY < minMenuY) { + nextTop = prevScrollTop + minMenuY; + } + } + if (nextSticky === true && stickyCache === false) { + menu.classList.add('sticky'); + stickyCache = true; + } else if (nextSticky === false && stickyCache === true) { + menu.classList.remove('sticky'); + stickyCache = false; + } + if (nextTop !== null) { + menu.style.top = nextTop + 'px'; + topCache = nextTop; + } + prevScrollTop = scrollTop; + }, { passive: true }); + })(); + (function controllBorder() { + menu.classList.remove('bordered'); + document.addEventListener('scroll', function () { + if (menu.offsetTop === 0) { + menu.classList.remove('bordered'); + } else { + menu.classList.add('bordered'); + } + }, { passive: true }); + })(); +})(); diff --git a/build-from-source.html b/build-from-source.html new file mode 100644 index 000000000000..b8380fe640a8 --- /dev/null +++ b/build-from-source.html @@ -0,0 +1,243 @@ + + + + + + Building from source - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Installing from source code

+
+

If you were able to install Kani normally, you do not need to build Kani from source. +You probably want to proceed to the Kani tutorial.

+
+

Dependencies

+

In general, the following dependencies are required to build Kani from source.

+
+

NOTE: These dependencies may be installed by running the scripts shown +below and don't need to be manually installed.

+
+
    +
  1. Cargo installed via rustup
  2. +
  3. CBMC (latest release)
  4. +
  5. Kissat (Release 4.0.1)
  6. +
+

Kani has been tested in Ubuntu and macOS platforms.

+

Install dependencies on Ubuntu

+

Support is available for Ubuntu 20.04, 22.04, and 24.04. +The simplest way to install dependencies (especially if you're using a fresh VM) +is following our CI scripts:

+
# git clone git@github.com:model-checking/kani.git
+git clone https://github.com/model-checking/kani.git
+cd kani
+git submodule update --init
+./scripts/setup/ubuntu/install_deps.sh
+# If you haven't already (or from https://rustup.rs/):
+./scripts/setup/install_rustup.sh
+source $HOME/.cargo/env
+
+

Install dependencies on macOS

+

Support is available for macOS 11. You need to have Homebrew installed already.

+
# git clone git@github.com:model-checking/kani.git
+git clone https://github.com/model-checking/kani.git
+cd kani
+git submodule update --init
+./scripts/setup/macos/install_deps.sh
+# If you haven't already (or from https://rustup.rs/):
+./scripts/setup/install_rustup.sh
+source $HOME/.cargo/env
+
+

Build and test Kani

+

Build the Kani package using:

+
cargo build-dev -- --release
+
+

to compile with optimizations turned on or using:

+
cargo build-dev
+
+

to compile in debug/development mode.

+

Then, optionally, run the regression tests:

+
./scripts/kani-regression.sh
+
+

This script has a lot of noisy output, but on a successful run you'll see at the end of the execution:

+
All Kani regression tests completed successfully.
+
+

Adding Kani to your path

+

To use a locally-built Kani from anywhere, add the Kani scripts to your path:

+
export PATH=$(pwd)/scripts:$PATH
+
+

Next steps

+

If you're learning Kani for the first time, you may be interested in our tutorial.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/cbmc-hacks.html b/cbmc-hacks.html new file mode 100644 index 000000000000..2c535c9ee1f3 --- /dev/null +++ b/cbmc-hacks.html @@ -0,0 +1,231 @@ + + + + + + Working with CBMC - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Working with CBMC

+

This section describes how to access more advanced CBMC options from Kani.

+

CBMC arguments

+

Kani is able to handle common CBMC arguments as if they were its own (e.g., +--default-unwind <n>), but sometimes it may be necessary to use CBMC arguments which +are not handled by Kani.

+

To pass additional arguments for CBMC, you pass --cbmc-args to Kani. Note that +this "switches modes" from Kani arguments to CBMC arguments: Any arguments that +appear after --cbmc-args are considered to be CBMC arguments, so all Kani +arguments must be placed before it.

+

Thus, the command line format to invoke cargo kani with CBMC arguments is:

+
cargo kani [<kani-args>]* --cbmc-args [<cbmc-args>]*
+
+
+

NOTE: In cases where CBMC is not expected to emit a verification output, +you have to use Kani's argument --output-format old to turn off the +post-processing of output from CBMC.

+
+

Individual loop bounds

+

Setting --default-unwind <n> affects every loop in a harness. +Once you know a particular loop is causing trouble, sometimes it can be helpful to provide a specific bound for it.

+

In the general case, specifying just the highest bound globally for all loops +shouldn't cause any problems, except that the solver may take more time because +all loops will be unwound to the specified bound.

+

In situations where you need to optimize for the solver, individual bounds for +each loop can be provided on the command line. To do so, we first need to know +the labels assigned to each loop with the CBMC argument --show-loops:

+
# kani src/lib.rs --output-format old --cbmc-args --show-loops
+[...]
+Loop _RNvCs6JP7pnlEvdt_3lib17initialize_prefix.0:
+  file ./src/lib.rs line 11 column 5 function initialize_prefix
+
+Loop _RNvMs8_NtNtCswN0xKFrR8r_4core3ops5rangeINtB5_14RangeInclusivejE8is_emptyCs6JP7pnlEvdt_3lib.0:
+  file $RUST/library/core/src/ops/range.rs line 540 column 9 function std::ops::RangeInclusive::<Idx>::is_empty
+
+Loop gen-repeat<[u8; 10]::16806744624734428132>.0:
+
+

This command shows us the labels of the loops involved. Note that, as mentioned +in CBMC arguments, we need to use --output-format old to +avoid post-processing the output from CBMC.

+
+

NOTE: At the moment, these labels are constructed using the mangled name +of the function and an index. Mangled names are likely to change across +different versions, so this method is highly unstable.

+
+

Then, we can use the CBMC argument --unwindset label_1:bound_1,label_2:bound_2,... to specify an individual bound for each +loop as follows:

+
kani src/lib.rs --cbmc-args --unwindset _RNvCs6JP7pnlEvdt_3lib17initialize_prefix.0:12
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/cheat-sheets.html b/cheat-sheets.html new file mode 100644 index 000000000000..89bab7e171d8 --- /dev/null +++ b/cheat-sheets.html @@ -0,0 +1,294 @@ + + + + + + Command cheat sheets - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Command cheat sheets

+

Development work in the Kani project depends on multiple tools. Regardless of +your familiarity with the project, the commands below may be useful for +development purposes.

+

Kani

+

Build

+
# Error "'rustc' panicked at 'failed to lookup `SourceFile` in new context'"
+# or similar error? Cleaning artifacts might help.
+# Otherwise, comment the line below.
+cargo clean
+cargo build-dev
+
+

Test

+
# Full regression suite
+./scripts/kani-regression.sh
+
+
# Delete regression test caches (Linux)
+rm -r build/x86_64-unknown-linux-gnu/tests/
+
+
# Delete regression test caches (macOS)
+rm -r build/x86_64-apple-darwin/tests/
+
+
# Test suite run (we can only run one at a time)
+# cargo run -p compiletest -- --suite ${suite} --mode ${mode}
+cargo run -p compiletest -- --suite kani --mode kani
+
+
# Build documentation
+cd docs
+./build-docs.sh
+
+

Debug

+

These can help understand what Kani is generating or encountering on an example or test file:

+
# Enable `debug!` macro logging output when running Kani:
+kani --debug file.rs
+
+
# Use KANI_LOG for a finer grain control of the source and verbosity of logs.
+# E.g.: The command below will print all logs from the kani_middle module.
+KANI_LOG="kani_compiler::kani_middle=trace" kani file.rs
+
+
# Keep CBMC Symbol Table and Goto-C output (.json and .goto)
+kani --keep-temps file.rs
+
+
# Generate "C code" from CBMC IR (.c)
+kani --gen-c file.rs
+
+
# Generate a ${INPUT}.kani.mir file with a human friendly MIR dump
+# for all items that are compiled to the respective goto-program.
+RUSTFLAGS="--emit mir" kani ${INPUT}.rs
+
+

The KANI_REACH_DEBUG environment variable can be used to debug Kani's reachability analysis. +If defined, Kani will generate a DOT graph ${INPUT}.dot with the graph traversed during reachability analysis. +If defined and not empty, the graph will be filtered to end at functions that contains the substring +from KANI_REACH_DEBUG.

+

Note that this will only work on debug builds.

+
# Generate a DOT graph ${INPUT}.dot with the graph traversed during reachability analysis
+KANI_REACH_DEBUG= kani ${INPUT}.rs
+
+# Generate a DOT graph ${INPUT}.dot with the sub-graph traversed during the reachability analysis
+# that connect to the given target.
+KANI_REACH_DEBUG="${TARGET_ITEM}" kani ${INPUT}.rs
+
+

CBMC

+
# See CBMC IR from a C file:
+goto-cc file.c -o file.out
+goto-instrument --print-internal-representation file.out
+# or (for json symbol table)
+cbmc --show-symbol-table --json-ui file.out
+# or (an alternative concise format)
+cbmc --show-goto-functions file.out
+
+
# Recover C from goto-c binary
+goto-instrument --dump-c file.out > file.gen.c
+
+

Git

+

The Kani project follows the squash and merge option for pull request merges. +As a result:

+
    +
  1. The title of your pull request will become the main commit message.
  2. +
  3. The messages from commits in your pull request will appear by default as a bulleted list in the main commit message body.
  4. +
+

But the main commit message body is editable at merge time, so you don't have to worry about "typo fix" messages because these can be removed before merging.

+
# Set up your git fork
+git remote add fork git@github.com:${USER}/kani.git
+
+
# Reset everything. Don't have any uncommitted changes!
+git clean -xffd
+git submodule foreach --recursive git clean -xffd
+git submodule update --init
+
+
# Need to update local branch (e.g. for an open pull request?)
+git fetch origin
+git merge origin/main
+# Or rebase, but that requires a force push,
+# and because we squash and merge, an extra merge commit in a PR doesn't hurt.
+
+
# Checkout a pull request locally without the github cli
+git fetch origin pull/$ID/head:pr/$ID
+git switch pr/$ID
+
+
# Push to someone else's pull request
+git origin add $USER $GIR_URL_FOR_THAT_USER
+git push $USER $LOCAL_BRANCH:$THEIR_PR_BRANCH_NAME
+
+
# Search only git-tracked files
+git grep codegen_panic
+
+
# Accidentally commit to main?
+# "Move" commit to a branch:
+git checkout -b my_branch
+# Fix main:
+git branch --force main origin/main
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/clipboard.min.js b/clipboard.min.js new file mode 100644 index 000000000000..02c549e35c86 --- /dev/null +++ b/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.4 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n + + + + + Coding conventions - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Coding conventions

+

Formatting

+

We automate most of our formatting preferences. Our CI will run format checkers for PRs and pushes. +These checks are required for merging any PR.

+

For Rust, we use rustfmt +which is configured via the rustfmt.toml file. +We are also in the process of enabling clippy. +Because of that, we still have a couple of lints disabled (see .cargo/config for the updated list).

+

We also have a bit of C and Python code in our repository. +For C we use clang-format and for Python scripts we use autopep8. +See .clang-format +and pyproject.toml +for their configuration.

+

Exceptions

+

We recognize that in some cases, the formatting and lints automation may not be applicable to a specific code. +In those cases, we usually prefer explicitly allowing exceptions by locally disabling the check. +E.g., use #[allow] annotation instead of disabling a link on a crate or project level.

+ +

All source code files begin with a copyright and license notice. If this is a new file, please add the following notice:

+
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+

When modifying a file from another project, please keep their headers as is and append the following notice after them:

+
// ... existing licensing headers ...
+
+// Modifications Copyright Kani Contributors
+// See GitHub history for details.
+
+

Note: The comment escape characters will depend on the type of file you are working with. E.g.: For rust start the +header with //, but for python start with #.

+

We also have automated checks for the copyright notice. +There are a few file types where this rule doesn't apply. +You can see that list in the copyright-exclude file.

+

Code for soundness

+

We are developing Kani to provide assurance that critical Rust components are verifiably free of certain classes of +security and correctness issues. +Thus, it is critical that we provide a verification tool that is sound. +For the class of errors that Kani can verify, we should not produce a "No Error" result if there was in fact an +error in the code being verified, i.e., it has no +"False Negatives".

+

Because of that, we bias on the side of correctness. +Any incorrect modeling +that may trigger an unsound analysis that cannot be fixed in the short term should be mitigated. +Here are a few ways how we do that.

+

Compilation errors

+

Make sure to add user-friendly errors for constructs that we can't handle. +For example, Kani cannot handle the panic unwind strategy, and it will fail compilation if the crate uses this +configuration.

+

In general, it's preferred that error messages follow these guidelines used for rustc development. +If the errors are being emitted from kani-compiler, you should use the compiler error message utilities (e.g., the Session::span_err method). However, if the +errors are being emitted from kani-driver, you should use the functions provided in the util module in kani-driver.

+

Internal compiler errors

+

Even though this doesn't provide users the best experience, you are encouraged to add checks in the compiler for any +assumptions you make during development. +Those checks can be on the form of assert!() or unreachable!() +statement. +Please provide a meaningful message to help user understand why something failed, and try to explain, at least with +a comment, why this is the case.

+

We don't formally use any specific formal representation of function contract, +but whenever possible we do instrument the code with assertions that may represent the function pre- and +post-conditions to ensure we are modeling the user code correctly.

+

Verification errors

+

In cases where Kani fails to model a certain instruction or local construct that doesn't have a global effect, +we encode this failure as a verification error. +I.e., we generate an assertion failure instead of the construct we are modeling using +codegen_unimplemented(), +which blocks the execution whenever this construct is reached.

+

This will allow users to verify their crate successfully as long as +that construct is not reachable in any harness. If a harness has at least one possible execution path that reaches +such construct, Kani will fail the verification, and it will mark all checks, other than failed checks, with +UNDETERMINED status.

+

Create detailed issues for "TODO" tasks

+

It is OK to add "TODO" comments as long as they don't compromise user experience or the tool correctness. +When doing so, please create an issue that captures the task. +Add details about the task at hand including any impact to the user. +Finally, add the link to the issue that captures the "TODO" task as part of your comment.

+

E.g.:

+
// TODO: This function assumes type cannot be ZST. Check if that's always the case.
+// https://github.com/model-checking/kani/issues/XXXX
+assert!(!typ.is_zst(), "Unexpected ZST type");
+
+

Performant but readable

+

We aim at writing code that is performant but also readable and easy to maintain. +Avoid compromising the code quality if the performance gain is not significant.

+

Here are few tips that can help the readability of your code:

+
    +
  • Sort match arms, enum variants, and struct fields alphabetically.
  • +
  • Prefer concise but meaningful names.
  • +
  • Prefer exhaustive matches.
  • +
  • Prefer declarative over imperative programming.
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/crates/.rustc_info.json b/crates/.rustc_info.json new file mode 100644 index 000000000000..22f974d85aa8 --- /dev/null +++ b/crates/.rustc_info.json @@ -0,0 +1 @@ +{"rustc_fingerprint":7283643872730095230,"outputs":{"14388650423374889093":{"success":true,"status":"","code":0,"stdout":"rustc 1.86.0-nightly (124cc9219 2025-02-09)\nbinary: rustc\ncommit-hash: 124cc92199ffa924f6b4c7cc819a85b65e0c3984\ncommit-date: 2025-02-09\nhost: x86_64-unknown-linux-gnu\nrelease: 1.86.0-nightly\nLLVM version: 19.1.7\n","stderr":""},"15992429099602374558":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/runner/.rustup/toolchains/nightly-2025-02-10-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\nkani\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"x87\"\ntarget_has_atomic\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"unknown\"\nub_checks\nunix\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/crates/.rustdoc_fingerprint.json b/crates/.rustdoc_fingerprint.json new file mode 100644 index 000000000000..02a31108b995 --- /dev/null +++ b/crates/.rustdoc_fingerprint.json @@ -0,0 +1 @@ +{"rustc_vv":"rustc 1.86.0-nightly (124cc9219 2025-02-09)\nbinary: rustc\ncommit-hash: 124cc92199ffa924f6b4c7cc819a85b65e0c3984\ncommit-date: 2025-02-09\nhost: x86_64-unknown-linux-gnu\nrelease: 1.86.0-nightly\nLLVM version: 19.1.7\n"} \ No newline at end of file diff --git a/crates/doc/.lock b/crates/doc/.lock new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/crates/doc/crates.js b/crates/doc/crates.js new file mode 100644 index 000000000000..c780c05bf815 --- /dev/null +++ b/crates/doc/crates.js @@ -0,0 +1,2 @@ +window.ALL_CRATES = ["kani"]; +//{"start":21,"fragment_lengths":[6]} \ No newline at end of file diff --git a/crates/doc/help.html b/crates/doc/help.html new file mode 100644 index 000000000000..a5dc8de3f86a --- /dev/null +++ b/crates/doc/help.html @@ -0,0 +1 @@ +Help

Rustdoc help

Back
\ No newline at end of file diff --git a/crates/doc/kani/all.html b/crates/doc/kani/all.html new file mode 100644 index 000000000000..994ea2a2dd79 --- /dev/null +++ b/crates/doc/kani/all.html @@ -0,0 +1 @@ +List of all items in this crate
\ No newline at end of file diff --git a/crates/doc/kani/arbitrary/index.html b/crates/doc/kani/arbitrary/index.html new file mode 100644 index 000000000000..de90701f68de --- /dev/null +++ b/crates/doc/kani/arbitrary/index.html @@ -0,0 +1,3 @@ +kani::arbitrary - Rust

Module arbitrary

Source
Expand description

This module introduces the Arbitrary trait as well as implementation for +primitive types and other std containers.

+
\ No newline at end of file diff --git a/crates/doc/kani/arbitrary/sidebar-items.js b/crates/doc/kani/arbitrary/sidebar-items.js new file mode 100644 index 000000000000..5244ce01ccff --- /dev/null +++ b/crates/doc/kani/arbitrary/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {}; \ No newline at end of file diff --git a/crates/doc/kani/arbitrary_ptr/enum.AllocationStatus.html b/crates/doc/kani/arbitrary_ptr/enum.AllocationStatus.html new file mode 100644 index 000000000000..a9e1aff69ed6 --- /dev/null +++ b/crates/doc/kani/arbitrary_ptr/enum.AllocationStatus.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../kani/enum.AllocationStatus.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/arbitrary_ptr/fn.pointer_generator.html b/crates/doc/kani/arbitrary_ptr/fn.pointer_generator.html new file mode 100644 index 000000000000..ab6a6cf0c987 --- /dev/null +++ b/crates/doc/kani/arbitrary_ptr/fn.pointer_generator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../kani/fn.pointer_generator.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/arbitrary_ptr/struct.ArbitraryPointer.html b/crates/doc/kani/arbitrary_ptr/struct.ArbitraryPointer.html new file mode 100644 index 000000000000..656534c5c522 --- /dev/null +++ b/crates/doc/kani/arbitrary_ptr/struct.ArbitraryPointer.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../kani/struct.ArbitraryPointer.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/arbitrary_ptr/struct.PointerGenerator.html b/crates/doc/kani/arbitrary_ptr/struct.PointerGenerator.html new file mode 100644 index 000000000000..edddd32c905f --- /dev/null +++ b/crates/doc/kani/arbitrary_ptr/struct.PointerGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../kani/struct.PointerGenerator.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/attr.ensures.html b/crates/doc/kani/attr.ensures.html new file mode 100644 index 000000000000..8ae1bb99f607 --- /dev/null +++ b/crates/doc/kani/attr.ensures.html @@ -0,0 +1,13 @@ +ensures in kani - Rust

Attribute Macro ensures

#[ensures]
Expand description

Add a postcondition to this function.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+

The contents of the attribute is a closure that captures the input values to +the annotated function and the input to the function is the return value of +the function passed by reference. All Rust syntax is supported, even calling +other functions, but the computations must be side effect free, e.g. it +cannot perform I/O or use mutable memory.

+

Kani requires each function that uses a contract (this attribute or +requires) to have at least one designated +proof_for_contract harness for checking the +contract.

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.loop_invariant.html b/crates/doc/kani/attr.loop_invariant.html new file mode 100644 index 000000000000..89b77d021667 --- /dev/null +++ b/crates/doc/kani/attr.loop_invariant.html @@ -0,0 +1,7 @@ +loop_invariant in kani - Rust

Attribute Macro loop_invariant

#[loop_invariant]
Expand description

Add a loop invariant to this loop.

+

The contents of the attribute is a condition that should be satisfied at the +beginning of every iteration of the loop. +All Rust syntax is supported, even calling other functions, but +the computations must be side effect free, e.g. it cannot perform I/O or use +mutable memory.

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.modifies.html b/crates/doc/kani/attr.modifies.html new file mode 100644 index 000000000000..f033e228ae4b --- /dev/null +++ b/crates/doc/kani/attr.modifies.html @@ -0,0 +1,13 @@ +modifies in kani - Rust

Attribute Macro modifies

#[modifies]
Expand description

Declaration of an explicit write-set for the annotated function.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+

The contents of the attribute is a series of comma-separated expressions referencing the +arguments of the function. Each expression is expected to return a pointer type, i.e. *const T, +*mut T, &T or &mut T. The pointed-to type must implement +Arbitrary.

+

All Rust syntax is supported, even calling other functions, but the computations must be side +effect free, e.g. it cannot perform I/O or use mutable memory.

+

Kani requires each function that uses a contract to have at least one designated +proof_for_contract harness for checking the +contract.

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.proof.html b/crates/doc/kani/attr.proof.html new file mode 100644 index 000000000000..40b17e90d0f0 --- /dev/null +++ b/crates/doc/kani/attr.proof.html @@ -0,0 +1,6 @@ +proof in kani - Rust

Attribute Macro proof

#[proof]
Expand description

Marks a Kani proof harness

+

For async harnesses, this will call block_on to drive the future to completion (see its documentation for more information).

+

If you want to spawn tasks in an async harness, you have to pass a schedule to the #[kani::proof] attribute, +e.g. #[kani::proof(schedule = kani::RoundRobin::default())].

+

This will wrap the async function in a call to block_on_with_spawn (see its documentation for more information).

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.proof_for_contract.html b/crates/doc/kani/attr.proof_for_contract.html new file mode 100644 index 000000000000..3346d8a3320f --- /dev/null +++ b/crates/doc/kani/attr.proof_for_contract.html @@ -0,0 +1,7 @@ +proof_for_contract in kani - Rust

Attribute Macro proof_for_contract

#[proof_for_contract]
Expand description

Designates this function as a harness to check a function contract.

+

The argument to this macro is the relative path (e.g. foo or +super::some_mod::foo or crate::SomeStruct::foo) to the function, the +contract of which should be checked.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.recursion.html b/crates/doc/kani/attr.recursion.html new file mode 100644 index 000000000000..e9d6873db0e7 --- /dev/null +++ b/crates/doc/kani/attr.recursion.html @@ -0,0 +1,5 @@ +recursion in kani - Rust

Attribute Macro recursion

#[recursion]
Expand description

Specifies that a function contains recursion for contract instrumentation.**

+

This attribute is only used for function-contract instrumentation. Kani uses +this annotation to identify recursive functions and properly instantiate +kani::any_modifies to check such functions using induction.

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.requires.html b/crates/doc/kani/attr.requires.html new file mode 100644 index 000000000000..d5c899566ca1 --- /dev/null +++ b/crates/doc/kani/attr.requires.html @@ -0,0 +1,12 @@ +requires in kani - Rust

Attribute Macro requires

#[requires]
Expand description

Add a precondition to this function.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+

The contents of the attribute is a condition over the input values to the +annotated function. All Rust syntax is supported, even calling other +functions, but the computations must be side effect free, e.g. it cannot +perform I/O or use mutable memory.

+

Kani requires each function that uses a contract (this attribute or +ensures) to have at least one designated +proof_for_contract harness for checking the +contract.

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.should_panic.html b/crates/doc/kani/attr.should_panic.html new file mode 100644 index 000000000000..d17704098eac --- /dev/null +++ b/crates/doc/kani/attr.should_panic.html @@ -0,0 +1,10 @@ +should_panic in kani - Rust

Attribute Macro should_panic

#[should_panic]
Expand description

Specifies that a proof harness is expected to panic.**

+

This attribute allows users to exercise negative verification. +It’s analogous to how +#[should_panic] +allows users to exercise negative testing +for Rust unit tests.

+

§Limitations

+

The #[kani::should_panic] attribute verifies that there are one or more failed checks related to panics. +At the moment, it’s not possible to pin it down to specific panics.

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.solver.html b/crates/doc/kani/attr.solver.html new file mode 100644 index 000000000000..2d4a541fee88 --- /dev/null +++ b/crates/doc/kani/attr.solver.html @@ -0,0 +1,4 @@ +solver in kani - Rust

Attribute Macro solver

#[solver]
Expand description

Select the SAT solver to use with CBMC for this harness

+

The attribute #[kani::solver(arg)] can only be used alongside #[kani::proof].

+

arg - name of solver, e.g. kissat

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.stub.html b/crates/doc/kani/attr.stub.html new file mode 100644 index 000000000000..8cf9bdb32857 --- /dev/null +++ b/crates/doc/kani/attr.stub.html @@ -0,0 +1,8 @@ +stub in kani - Rust

Attribute Macro stub

#[stub]
Expand description

Specify a function/method stub pair to use for proof harness

+

The attribute #[kani::stub(original, replacement)] can only be used alongside #[kani::proof].

+

§Arguments

+
    +
  • original - The function or method to replace, specified as a path.
  • +
  • replacement - The function or method to use as a replacement, specified as a path.
  • +
+
\ No newline at end of file diff --git a/crates/doc/kani/attr.stub_verified.html b/crates/doc/kani/attr.stub_verified.html new file mode 100644 index 000000000000..3599615cae34 --- /dev/null +++ b/crates/doc/kani/attr.stub_verified.html @@ -0,0 +1,11 @@ +stub_verified in kani - Rust

Attribute Macro stub_verified

#[stub_verified]
Expand description

stub_verified(TARGET) is a harness attribute (to be used on +proof or proof_for_contract +function) that replaces all occurrences of TARGET reachable from this +harness with a stub generated from the contract on TARGET.

+

The target of stub_verified must have a contract. More information about +how to specify a contract for your function can be found +here.

+

You may use multiple stub_verified attributes on a single harness.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+
\ No newline at end of file diff --git a/crates/doc/kani/attr.unwind.html b/crates/doc/kani/attr.unwind.html new file mode 100644 index 000000000000..8b5d20d04804 --- /dev/null +++ b/crates/doc/kani/attr.unwind.html @@ -0,0 +1,4 @@ +unwind in kani - Rust

Attribute Macro unwind

#[unwind]
Expand description

Set Loop unwind limit for proof harnesses +The attribute #[kani::unwind(arg)] can only be called alongside #[kani::proof]. +arg - Takes in a integer value (u32) that represents the unwind value for the harness.

+
\ No newline at end of file diff --git a/crates/doc/kani/contracts/attr.ensures.html b/crates/doc/kani/contracts/attr.ensures.html new file mode 100644 index 000000000000..367c95b93db3 --- /dev/null +++ b/crates/doc/kani/contracts/attr.ensures.html @@ -0,0 +1,13 @@ +ensures in kani::contracts - Rust

Attribute Macro ensures

#[ensures]
Expand description

Add a postcondition to this function.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+

The contents of the attribute is a closure that captures the input values to +the annotated function and the input to the function is the return value of +the function passed by reference. All Rust syntax is supported, even calling +other functions, but the computations must be side effect free, e.g. it +cannot perform I/O or use mutable memory.

+

Kani requires each function that uses a contract (this attribute or +requires) to have at least one designated +proof_for_contract harness for checking the +contract.

+
\ No newline at end of file diff --git a/crates/doc/kani/contracts/attr.modifies.html b/crates/doc/kani/contracts/attr.modifies.html new file mode 100644 index 000000000000..ee2b078257f4 --- /dev/null +++ b/crates/doc/kani/contracts/attr.modifies.html @@ -0,0 +1,13 @@ +modifies in kani::contracts - Rust

Attribute Macro modifies

#[modifies]
Expand description

Declaration of an explicit write-set for the annotated function.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+

The contents of the attribute is a series of comma-separated expressions referencing the +arguments of the function. Each expression is expected to return a pointer type, i.e. *const T, +*mut T, &T or &mut T. The pointed-to type must implement +Arbitrary.

+

All Rust syntax is supported, even calling other functions, but the computations must be side +effect free, e.g. it cannot perform I/O or use mutable memory.

+

Kani requires each function that uses a contract to have at least one designated +proof_for_contract harness for checking the +contract.

+
\ No newline at end of file diff --git a/crates/doc/kani/contracts/attr.proof_for_contract.html b/crates/doc/kani/contracts/attr.proof_for_contract.html new file mode 100644 index 000000000000..2e878b5d9cc4 --- /dev/null +++ b/crates/doc/kani/contracts/attr.proof_for_contract.html @@ -0,0 +1,7 @@ +proof_for_contract in kani::contracts - Rust

Attribute Macro proof_for_contract

#[proof_for_contract]
Expand description

Designates this function as a harness to check a function contract.

+

The argument to this macro is the relative path (e.g. foo or +super::some_mod::foo or crate::SomeStruct::foo) to the function, the +contract of which should be checked.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+
\ No newline at end of file diff --git a/crates/doc/kani/contracts/attr.requires.html b/crates/doc/kani/contracts/attr.requires.html new file mode 100644 index 000000000000..551a5e1b021b --- /dev/null +++ b/crates/doc/kani/contracts/attr.requires.html @@ -0,0 +1,12 @@ +requires in kani::contracts - Rust

Attribute Macro requires

#[requires]
Expand description

Add a precondition to this function.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+

The contents of the attribute is a condition over the input values to the +annotated function. All Rust syntax is supported, even calling other +functions, but the computations must be side effect free, e.g. it cannot +perform I/O or use mutable memory.

+

Kani requires each function that uses a contract (this attribute or +ensures) to have at least one designated +proof_for_contract harness for checking the +contract.

+
\ No newline at end of file diff --git a/crates/doc/kani/contracts/attr.stub_verified.html b/crates/doc/kani/contracts/attr.stub_verified.html new file mode 100644 index 000000000000..de56c8794265 --- /dev/null +++ b/crates/doc/kani/contracts/attr.stub_verified.html @@ -0,0 +1,11 @@ +stub_verified in kani::contracts - Rust

Attribute Macro stub_verified

#[stub_verified]
Expand description

stub_verified(TARGET) is a harness attribute (to be used on +proof or proof_for_contract +function) that replaces all occurrences of TARGET reachable from this +harness with a stub generated from the contract on TARGET.

+

The target of stub_verified must have a contract. More information about +how to specify a contract for your function can be found +here.

+

You may use multiple stub_verified attributes on a single harness.

+

This is part of the function contract API, for more general information see +the module-level documentation.

+
\ No newline at end of file diff --git a/crates/doc/kani/contracts/index.html b/crates/doc/kani/contracts/index.html new file mode 100644 index 000000000000..21545375ee0d --- /dev/null +++ b/crates/doc/kani/contracts/index.html @@ -0,0 +1,200 @@ +kani::contracts - Rust

Module contracts

Source
Expand description

Kani implementation of function contracts.

+

Function contracts are still under development. Using the APIs therefore +requires the unstable -Zfunction-contracts flag to be passed. You can join +the discussion on contract design by reading our +RFC +and commenting on the tracking +issue.

+

The function contract API is expressed as proc-macro attributes, and there +are two parts to it.

+
    +
  1. Contract specification attributes: +requires and ensures.
  2. +
  3. Contract use attributes: +proof_for_contract and +stub_verified.
  4. +
+

§Step-by-step Guide

+

Let us explore using a workflow involving contracts on the example of a +simple division function my_div:

+ +
fn my_div(dividend: usize, divisor: usize) -> usize {
+  dividend / divisor
+}
+

With the contract specification attributes we can specify the behavior of +this function declaratively. The requires attribute +allows us to declare constraints on what constitutes valid inputs to our +function. In this case we would want to disallow a divisor that is 0.

+ +
#[requires(divisor != 0)]
+

This is called a precondition, because it is enforced before (pre-) the +function call. As you can see attribute has access to the functions +arguments. The condition itself is just regular Rust code. You can use any +Rust code, including calling functions and methods. However you may not +perform I/O (like println!) or mutate memory (like Vec::push).

+

The ensures attribute on the other hand lets us describe +the output value in terms of the inputs. You may be as (im)precise as you +like in the ensures clause, depending on your needs. One +approximation of the result of division for instance could be this:

+ +
#[ensures(|result : &usize| *result <= dividend)]
+

This is called a postcondition and it also has access to the arguments and +is expressed in regular Rust code. The same restrictions apply as did for +requires. In addition to the postcondition is expressed +as a closure where the value returned from the function is passed to this +closure by reference.

+

You may combine as many requires and +ensures attributes on a single function as you please. +They all get enforced (as if their conditions were &&ed together) and the +order does not matter. In our example putting them together looks like this:

+ +
use kani::{requires, ensures};
+
+#[requires(divisor != 0)]
+#[ensures(|result : &usize| *result <= dividend)]
+fn my_div(dividend: usize, divisor: usize) -> usize {
+  dividend / divisor
+}
+

Once we are finished specifying our contract we can ask Kani to check it’s +validity. For this we need to provide a proof harness that exercises the +function. The harness is created like any other, e.g. as a test-like +function with inputs and using kani::any to create arbitrary values. +However we do not need to add any assertions or assumptions about the +inputs, Kani will use the pre- and postconditions we have specified for that +and we use the proof_for_contract attribute +instead of proof and provide it with the path to the +function we want to check.

+ +
#[kani::proof_for_contract(my_div)]
+fn my_div_harness() {
+    my_div(kani::any(), kani::any());
+}
+

The harness is checked like any other by running cargo kani and can be +specifically selected with --harness my_div_harness.

+

Once we have verified that our contract holds, we can use perhaps it’s +coolest feature: verified stubbing. This allows us to use the conditions of +the contract instead of it’s implementation. This can be very powerful for +expensive implementations (involving loops for instance).

+

Verified stubbing is available to any harness via the +stub_verified harness attribute. We must provide +the attribute with the path to the function to stub, but unlike with +stub we do not need to provide a function to replace with, +the contract will be used automatically.

+ +
#[kani::proof]
+#[kani::stub_verified(my_div)]
+fn use_div() {
+  let v = kani::vec::any_vec::<char, 5>();
+  let some_idx = my_div(v.len() - 1, 3);
+  v[some_idx];
+}
+

In this example the contract is sufficient to prove that the element access +in the last line cannot be out-of-bounds.

+

§Specification Attributes Overview

+

The basic two specification attributes available for describing +function behavior are requires for preconditions and +ensures for postconditions. Both admit arbitrary Rust +expressions as their bodies which may also reference the function arguments +but must not mutate memory or perform I/O. The postcondition may +additionally reference the return value of the function as the variable +result.

+

In addition Kani provides the modifies attribute. This +works a bit different in that it does not contain conditions but a comma +separated sequence of expressions that evaluate to pointers. This attribute +constrains to which memory locations the function is allowed to write. Each +expression can contain arbitrary Rust syntax, though it may not perform side +effects and it is also currently unsound if the expression can panic. For more +information see the write sets section.

+

During verified stubbing the return value of a function with a contract is +replaced by a call to kani::any. As such the return value must implement +the kani::Arbitrary trait.

+

In Kani, function contracts are optional. As such a function with at least +one specification attribute is considered to “have a contract” and any +absent specification type defaults to its most general interpretation +(true). All functions with not a single specification attribute are +considered “not to have a contract” and are ineligible for use as the target +of a proof_for_contract of +stub_verified attribute.

+

§Contract Use Attributes Overview

+

Contract are used both to verify function behavior and to leverage the +verification result as a sound abstraction.

+

Verifying function behavior currently requires the designation of at least +one checking harness with the +proof_for_contract attribute. A harness may +only have one proof_for_contract attribute and it may not also have a +proof attribute.

+

The checking harness is expected to set up the arguments that foo should +be called with and initialized any static mut globals that are reachable. +All of these should be initialized to as general value as possible, usually +achieved using kani::any. The harness must call e.g. foo at least once +and if foo has type parameters, only one instantiation of those parameters +is admissible. Violating either results in a compile error.

+

If any inputs have special invariants you can use kani::assume to +enforce them but this may introduce unsoundness. In general all restrictions +on input parameters should be part of the requires +clause of the function contract.

+

Once the contract has been verified it may be used as a verified stub. For +this the stub_verified attribute is used. +stub_verified is a harness attribute, like +unwind, meaning it is used on functions that are +annotated with proof. It may also be used on a +proof_for_contract proof.

+

Unlike proof_for_contract multiple stub_verified attributes are allowed +on the same proof harness though they must target different functions.

+

§Inductive Verification

+

Function contracts by default use inductive verification to efficiently +verify recursive functions. In inductive verification a recursive function +is executed once and every recursive call instead uses the contract +replacement. In this way many recursive calls can be checked with a +single verification pass.

+

The downside of inductive verification is that the return value of a +contracted function must implement kani::Arbitrary. Due to restrictions to +code generation in proc macros, the contract macros cannot determine reliably +in all cases whether a given function with a contract is recursive. As a +result it conservatively sets up inductive verification for every function +and requires the kani::Arbitrary constraint for contract checks.

+

If you feel strongly about this issue you can join the discussion on issue +#2823 to enable +opt-out of inductive verification.

+

§Write Sets

+

The modifies attribute is used to describe which +locations in memory a function may assign to. The attribute contains a comma +separated series of expressions that reference the function arguments. +Syntactically any expression is permissible, though it may not perform side +effects (I/O, mutation) or panic. As an example consider this super simple +function:

+ +
#[kani::modifies(ptr, my_box.as_ref())]
+fn a_function(ptr: &mut u32, my_box: &mut Box<u32>) {
+    *ptr = 80;
+    *my_box.as_mut() = 90;
+}
+

Because the function performs an observable side-effect (setting both the +value behind the pointer and the value pointed-to by the box) we need to +provide a modifies attribute. Otherwise Kani will reject a contract on +this function.

+

An expression used in a modifies clause must return a pointer to the +location that you would like to allow to be modified. This can be any basic +Rust pointer type (&T, &mut T, *const T or *mut T). In addition T +must implement Arbitrary. This is used to assign +kani::any() to the location when the function is used in a stub_verified.

+

§History Expressions

+

Additionally, an ensures clause is allowed to refer to the state of the function arguments before function execution and perform simple computations on them +via an old monad. Any instance of old(computation) will evaluate the +computation before the function is called. It is required that this computation +is effect free and closed with respect to the function arguments.

+

For example, the following code passes kani tests:

+ +
#[kani::modifies(a)]
+#[kani::ensures(|result| old(*a).wrapping_add(1) == *a)]
+#[kani::ensures(|result : &u32| old(*a).wrapping_add(1) == *result)]
+fn add1(a : &mut u32) -> u32 {
+    *a=a.wrapping_add(1);
+    *a
+}
+

Here, the value stored in a is precomputed and remembered after the function +is called, even though the contents of a changed during the function execution.

+

Attribute Macros§

ensures
Add a postcondition to this function.
modifies
Declaration of an explicit write-set for the annotated function.
proof_for_contract
Designates this function as a harness to check a function contract.
requires
Add a precondition to this function.
stub_verified
stub_verified(TARGET) is a harness attribute (to be used on +proof or proof_for_contract +function) that replaces all occurrences of TARGET reachable from this +harness with a stub generated from the contract on TARGET.
\ No newline at end of file diff --git a/crates/doc/kani/contracts/sidebar-items.js b/crates/doc/kani/contracts/sidebar-items.js new file mode 100644 index 000000000000..359015553659 --- /dev/null +++ b/crates/doc/kani/contracts/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"attr":["ensures","modifies","proof_for_contract","requires","stub_verified"]}; \ No newline at end of file diff --git a/crates/doc/kani/derive.Arbitrary.html b/crates/doc/kani/derive.Arbitrary.html new file mode 100644 index 000000000000..75a5afc80042 --- /dev/null +++ b/crates/doc/kani/derive.Arbitrary.html @@ -0,0 +1,85 @@ +Arbitrary in kani - Rust

Derive Macro Arbitrary

#[derive(Arbitrary)]
+{
+    // Attributes available to this derive:
+    #[safety_constraint]
+}
+
Expand description

Allow users to auto generate Arbitrary implementations by using +#[derive(Arbitrary)] macro.

+

§Type safety specification with the #[safety_constraint(...)] attribute

+

When using #[derive(Arbitrary)] on a struct, the +#[safety_constraint(<cond>)] attribute can be added to either the struct +or its fields (but not both) to indicate a type safety invariant condition +<cond>. Since kani::any() is always expected to produce type-safe +values, adding #[safety_constraint(...)] to the struct or any of its +fields will further constrain the objects generated with kani::any().

+

For example, the check_positive harness in this code is expected to +pass:

+ +
#[derive(kani::Arbitrary)]
+struct AlwaysPositive {
+    #[safety_constraint(*inner >= 0)]
+    inner: i32,
+}
+
+#[kani::proof]
+fn check_positive() {
+    let val: AlwaysPositive = kani::any();
+    assert!(val.inner >= 0);
+}
+

But using the #[safety_constraint(...)] attribute can lead to vacuous +results when the values are over-constrained. For example, in this code +the check_positive harness will pass too:

+ +
#[derive(kani::Arbitrary)]
+struct AlwaysPositive {
+    #[safety_constraint(*inner >= 0 && *inner < i32::MIN)]
+    inner: i32,
+}
+
+#[kani::proof]
+fn check_positive() {
+    let val: AlwaysPositive = kani::any();
+    assert!(val.inner >= 0);
+}
+

Unfortunately, we made a mistake when specifying the condition because +*inner >= 0 && *inner < i32::MIN is equivalent to false. This results +in the relevant assertion being unreachable:

+ +
Check 1: check_positive.assertion.1
+        - Status: UNREACHABLE
+        - Description: "assertion failed: val.inner >= 0"
+        - Location: src/main.rs:22:5 in function check_positive
+

As usual, we recommend users to defend against these behaviors by using +kani::cover!(...) checks and watching out for unreachable assertions in +their project’s code.

+

§Adding #[safety_constraint(...)] to the struct as opposed to its fields

+

As mentioned earlier, the #[safety_constraint(...)] attribute can be added +to either the struct or its fields, but not to both. Adding the +#[safety_constraint(...)] attribute to both the struct and its fields will +result in an error.

+

In practice, only one type of specification is need. If the condition for +the type safety invariant involves a relation between two or more struct +fields, the struct-level attribute should be used. Otherwise, using the +#[safety_constraint(...)] on field(s) is recommended since it helps with readability.

+

For example, if we were defining a custom vector MyVector and wanted to +specify that the inner vector’s length is always less than or equal to its +capacity, we should do it as follows:

+ +
#[derive(Arbitrary)]
+#[safety_constraint(vector.len() <= *capacity)]
+struct MyVector<T> {
+    vector: Vec<T>,
+    capacity: usize,
+}
+

However, if we were defining a struct whose fields are not related in any +way, we would prefer using the #[safety_constraint(...)] attribute on its +fields:

+ +
#[derive(Arbitrary)]
+struct PositivePoint {
+    #[safety_constraint(*x >= 0)]
+    x: i32,
+    #[safety_constraint(*y >= 0)]
+    y: i32,
+}
+
\ No newline at end of file diff --git a/crates/doc/kani/derive.Invariant.html b/crates/doc/kani/derive.Invariant.html new file mode 100644 index 000000000000..d761663c4cf9 --- /dev/null +++ b/crates/doc/kani/derive.Invariant.html @@ -0,0 +1,77 @@ +Invariant in kani - Rust

Derive Macro Invariant

#[derive(Invariant)]
+{
+    // Attributes available to this derive:
+    #[safety_constraint]
+}
+
Expand description

Allow users to auto generate Invariant implementations by using +#[derive(Invariant)] macro.

+

§Type safety specification with the #[safety_constraint(...)] attribute

+

When using #[derive(Invariant)] on a struct, the +#[safety_constraint(<cond>)] attribute can be added to either the struct +or its fields (but not both) to indicate a type safety invariant condition +<cond>. This will ensure that the type-safety condition gets additionally +checked when using the is_safe() method automatically generated by the +#[derive(Invariant)] macro.

+

For example, the check_positive harness in this code is expected to +fail:

+ +
#[derive(kani::Invariant)]
+struct AlwaysPositive {
+    #[safety_constraint(*inner >= 0)]
+    inner: i32,
+}
+
+#[kani::proof]
+fn check_positive() {
+    let val = AlwaysPositive { inner: -1 };
+    assert!(val.is_safe());
+}
+

This is not too surprising since the type safety invariant that we indicated +is not being taken into account when we create the AlwaysPositive object.

+

As mentioned, the is_safe() methods generated by the +#[derive(Invariant)] macro check the corresponding is_safe() method for +each field in addition to any type safety invariants specified through the +#[safety_constraint(...)] attribute.

+

For example, for the AlwaysPositive struct from above, we will generate +the following implementation:

+ +
impl kani::Invariant for AlwaysPositive {
+    fn is_safe(&self) -> bool {
+        let obj = self;
+        let inner = &obj.inner;
+        true && *inner >= 0 && inner.is_safe()
+    }
+}
+

Note: the assignments to obj and inner are made so that we can treat the +fields as if they were references.

+

§Adding #[safety_constraint(...)] to the struct as opposed to its fields

+

As mentioned earlier, the #[safety_constraint(...)] attribute can be added +to either the struct or its fields, but not to both. Adding the +#[safety_constraint(...)] attribute to both the struct and its fields will +result in an error.

+

In practice, only one type of specification is need. If the condition for +the type safety invariant involves a relation between two or more struct +fields, the struct-level attribute should be used. Otherwise, using the +#[safety_constraint(...)] is recommended since it helps with readability.

+

For example, if we were defining a custom vector MyVector and wanted to +specify that the inner vector’s length is always less than or equal to its +capacity, we should do it as follows:

+ +
#[derive(Invariant)]
+#[safety_constraint(vector.len() <= *capacity)]
+struct MyVector<T> {
+    vector: Vec<T>,
+    capacity: usize,
+}
+

However, if we were defining a struct whose fields are not related in any +way, we would prefer using the #[safety_constraint(...)] attribute on its +fields:

+ +
#[derive(Invariant)]
+struct PositivePoint {
+    #[safety_constraint(*x >= 0)]
+    x: i32,
+    #[safety_constraint(*y >= 0)]
+    y: i32,
+}
+
\ No newline at end of file diff --git a/crates/doc/kani/enum.AllocationStatus.html b/crates/doc/kani/enum.AllocationStatus.html new file mode 100644 index 000000000000..049ffc93d223 --- /dev/null +++ b/crates/doc/kani/enum.AllocationStatus.html @@ -0,0 +1,27 @@ +AllocationStatus in kani - Rust

Enum AllocationStatus

Source
pub enum AllocationStatus {
+    Dangling,
+    DeadObject,
+    Null,
+    InBounds,
+    OutOfBounds,
+}
Expand description

Enumeration with the cases currently covered by the pointer generator.

+

Variants§

§

Dangling

Dangling pointers

+
§

DeadObject

Pointer to dead object

+
§

Null

Null pointers

+
§

InBounds

In bounds pointer (it may be unaligned)

+
§

OutOfBounds

The pointer cannot be read / written to for the given type since one or more bytes +would be out of bounds of the current allocation.

+

Trait Implementations§

Source§

impl Arbitrary for AllocationStatus

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Clone for AllocationStatus

Source§

fn clone(&self) -> AllocationStatus

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for AllocationStatus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for AllocationStatus

Source§

fn eq(&self, other: &AllocationStatus) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for AllocationStatus

Source§

impl Eq for AllocationStatus

Source§

impl StructuralPartialEq for AllocationStatus

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dst: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/crates/doc/kani/float/fn.float_to_int_in_range.html b/crates/doc/kani/float/fn.float_to_int_in_range.html new file mode 100644 index 000000000000..e1982b80e0e2 --- /dev/null +++ b/crates/doc/kani/float/fn.float_to_int_in_range.html @@ -0,0 +1,15 @@ +float_to_int_in_range in kani::float - Rust

Function float_to_int_in_range

Source
pub fn float_to_int_in_range<Float, Int>(value: Float) -> bool
where + Float: FloatToInt<Int>,
Expand description

Returns whether the given float value satisfies the range +condition of the to_int_unchecked methods, namely that the value +after truncation is in range of the target Int

+

§Example:

+
let f: f32 = 145.7;
+let fits_in_i8 = kani::float::float_to_int_in_range::<f32, i8>(f);
+// doesn't fit in `i8` because the value after truncation (`145.0`) is larger than `i8::MAX`
+assert!(!fits_in_i8);
+
+let f: f64 = 1e6;
+let fits_in_u32 = kani::float::float_to_int_in_range::<f64, u32>(f);
+// fits in `u32` because the value after truncation (`1e6`) is smaller than `u32::MAX`
+assert!(fits_in_u32);
+
\ No newline at end of file diff --git a/crates/doc/kani/float/index.html b/crates/doc/kani/float/index.html new file mode 100644 index 000000000000..1ecdbceab847 --- /dev/null +++ b/crates/doc/kani/float/index.html @@ -0,0 +1,4 @@ +kani::float - Rust

Module float

Source
Expand description

This module contains functions useful for float-related checks

+

Functions§

float_to_int_in_range
Returns whether the given float value satisfies the range +condition of the to_int_unchecked methods, namely that the value +after truncation is in range of the target Int
\ No newline at end of file diff --git a/crates/doc/kani/float/sidebar-items.js b/crates/doc/kani/float/sidebar-items.js new file mode 100644 index 000000000000..fb73e65a7ea1 --- /dev/null +++ b/crates/doc/kani/float/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["float_to_int_in_range"]}; \ No newline at end of file diff --git a/crates/doc/kani/fn.any.html b/crates/doc/kani/fn.any.html new file mode 100644 index 000000000000..04ffc91ce484 --- /dev/null +++ b/crates/doc/kani/fn.any.html @@ -0,0 +1,12 @@ +any in kani - Rust

Function any

Source
pub fn any<T: Arbitrary>() -> T
Expand description

This creates an symbolic valid value of type T. You can assign the return value of this +function to a variable that you want to make symbolic.

+

§Example:

+

In the snippet below, we are verifying the behavior of the function fn_under_verification +under all possible NonZeroU8 input values, i.e., all possible u8 values except zero.

+ +
let inputA = kani::any::<core::num::NonZeroU8>();
+fn_under_verification(inputA);
+

Note: This is a safe construct and can only be used with types that implement the Arbitrary +trait. The Arbitrary trait is used to build a symbolic value that represents all possible +valid values for type T.

+
\ No newline at end of file diff --git a/crates/doc/kani/fn.any_where.html b/crates/doc/kani/fn.any_where.html new file mode 100644 index 000000000000..537e5291834a --- /dev/null +++ b/crates/doc/kani/fn.any_where.html @@ -0,0 +1,13 @@ +any_where in kani - Rust

Function any_where

Source
pub fn any_where<T: Arbitrary, F: FnOnce(&T) -> bool>(f: F) -> T
Expand description

This creates a symbolic valid value of type T. +The value is constrained to be a value accepted by the predicate passed to the filter. +You can assign the return value of this function to a variable that you want to make symbolic.

+

§Example:

+

In the snippet below, we are verifying the behavior of the function fn_under_verification +under all possible u8 input values between 0 and 12.

+ +
let inputA: u8 = kani::any_where(|x| *x < 12);
+fn_under_verification(inputA);
+

Note: This is a safe construct and can only be used with types that implement the Arbitrary +trait. The Arbitrary trait is used to build a symbolic value that represents all possible +valid values for type T.

+
\ No newline at end of file diff --git a/crates/doc/kani/fn.assert.html b/crates/doc/kani/fn.assert.html new file mode 100644 index 000000000000..a16db24e1c49 --- /dev/null +++ b/crates/doc/kani/fn.assert.html @@ -0,0 +1,6 @@ +assert in kani - Rust

Function assert

Source
pub const fn assert(cond: bool, msg: &'static str)
Expand description

Creates an assertion of the specified condition and message.

+

§Example:

+
let x: bool = kani::any();
+let y = !x;
+kani::assert(x || y, "ORing a boolean variable with its negation must be true")
+
\ No newline at end of file diff --git a/crates/doc/kani/fn.assume.html b/crates/doc/kani/fn.assume.html new file mode 100644 index 000000000000..a6bfdf4efb3b --- /dev/null +++ b/crates/doc/kani/fn.assume.html @@ -0,0 +1,17 @@ +assume in kani - Rust

Function assume

Source
pub fn assume(cond: bool)
Expand description

Creates an assumption that will be valid after this statement run. Note that the assumption +will only be applied for paths that follow the assumption. If the assumption doesn’t hold, the +program will exit successfully.

+

§Example:

+

The code snippet below should never panic.

+ +
let i : i32 = kani::any();
+kani::assume(i > 10);
+if i < 0 {
+  panic!("This will never panic");
+}
+

The following code may panic though:

+ +
let i : i32 = kani::any();
+assert!(i < 0, "This may panic and verification should fail.");
+kani::assume(i > 10);
+
\ No newline at end of file diff --git a/crates/doc/kani/fn.concrete_playback_run.html b/crates/doc/kani/fn.concrete_playback_run.html new file mode 100644 index 000000000000..539b3c8044f9 --- /dev/null +++ b/crates/doc/kani/fn.concrete_playback_run.html @@ -0,0 +1,2 @@ +concrete_playback_run in kani - Rust

Function concrete_playback_run

Source
pub fn concrete_playback_run<F: Fn()>(_: Vec<Vec<u8>>, _: F)
Expand description

NOP concrete_playback for type checking during verification mode.

+
\ No newline at end of file diff --git a/crates/doc/kani/fn.cover.html b/crates/doc/kani/fn.cover.html new file mode 100644 index 000000000000..69efa370d0bf --- /dev/null +++ b/crates/doc/kani/fn.cover.html @@ -0,0 +1,14 @@ +cover in kani - Rust

Function cover

Source
pub const fn cover(_cond: bool, _msg: &'static str)
Expand description

Creates a cover property with the specified condition and message.

+

§Example:

+
kani::cover(slice.len() == 0, "The slice may have a length of 0");
+

A cover property checks if there is at least one execution that satisfies +the specified condition at the location in which the function is called.

+

Cover properties are reported as:

+
    +
  • SATISFIED: if Kani found an execution that satisfies the condition
  • +
  • UNSATISFIABLE: if Kani proved that the condition cannot be satisfied
  • +
  • UNREACHABLE: if Kani proved that the cover property itself is unreachable (i.e. it is vacuously UNSATISFIABLE)
  • +
+

This function is called by the cover! macro. The macro is more +convenient to use.

+
\ No newline at end of file diff --git a/crates/doc/kani/fn.pointer_generator.html b/crates/doc/kani/fn.pointer_generator.html new file mode 100644 index 000000000000..5055bd3b99f0 --- /dev/null +++ b/crates/doc/kani/fn.pointer_generator.html @@ -0,0 +1,2 @@ +pointer_generator in kani - Rust

Function pointer_generator

Source
pub fn pointer_generator<T, const NUM_ELTS: usize>() -> PointerGenerator<{ _ }>
Expand description

Create a pointer generator that fits at least N elements of type T.

+
\ No newline at end of file diff --git a/crates/doc/kani/futures/enum.SchedulingAssumption.html b/crates/doc/kani/futures/enum.SchedulingAssumption.html new file mode 100644 index 000000000000..69f326f229d8 --- /dev/null +++ b/crates/doc/kani/futures/enum.SchedulingAssumption.html @@ -0,0 +1,17 @@ +SchedulingAssumption in kani::futures - Rust

Enum SchedulingAssumption

Source
pub enum SchedulingAssumption {
+    CanAssumeRunning,
+    CannotAssumeRunning,
+}
Expand description

Indicates to the scheduler whether it can kani::assume that the returned task is running.

+

This is useful if the task was picked nondeterministically using kani::any(). +For more information, see SchedulingStrategy.

+

Variants§

§

CanAssumeRunning

§

CannotAssumeRunning

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/crates/doc/kani/futures/fn.block_on.html b/crates/doc/kani/futures/fn.block_on.html new file mode 100644 index 000000000000..52f3ff55f03b --- /dev/null +++ b/crates/doc/kani/futures/fn.block_on.html @@ -0,0 +1,7 @@ +block_on in kani::futures - Rust

Function block_on

Source
pub fn block_on<T>(fut: impl Future<Output = T>) -> T
Expand description

A very simple executor: it polls the future in a busy loop until completion

+

This is intended as a drop-in replacement for futures::block_on, which Kani cannot handle. +Whereas a clever executor like block_on in futures or tokio would interact with the OS scheduler +to be woken up when a resource becomes available, this is not supported by Kani. +As a consequence, this function completely ignores the waker infrastructure and just polls the given future in a busy loop.

+

Note that spawn is not supported with this function. Use block_on_with_spawn if you need it.

+
\ No newline at end of file diff --git a/crates/doc/kani/futures/fn.block_on_with_spawn.html b/crates/doc/kani/futures/fn.block_on_with_spawn.html new file mode 100644 index 000000000000..2668085bb1c4 --- /dev/null +++ b/crates/doc/kani/futures/fn.block_on_with_spawn.html @@ -0,0 +1,6 @@ +block_on_with_spawn in kani::futures - Rust

Function block_on_with_spawn

Source
pub fn block_on_with_spawn<F: Future<Output = ()> + Sync + 'static>(
+    fut: F,
+    scheduling_plan: impl SchedulingStrategy,
+)
Expand description

Polls the given future and the tasks it may spawn until all of them complete

+

Contrary to block_on, this allows spawning other futures

+
\ No newline at end of file diff --git a/crates/doc/kani/futures/fn.spawn.html b/crates/doc/kani/futures/fn.spawn.html new file mode 100644 index 000000000000..31c6014c29ef --- /dev/null +++ b/crates/doc/kani/futures/fn.spawn.html @@ -0,0 +1,3 @@ +spawn in kani::futures - Rust

Function spawn

Source
pub fn spawn<F: Future<Output = ()> + Sync + 'static>(fut: F) -> JoinHandle 
Expand description

Spawns a task on the current global executor (which is set by block_on_with_spawn)

+

This function can only be called inside a future passed to block_on_with_spawn.

+
\ No newline at end of file diff --git a/crates/doc/kani/futures/fn.yield_now.html b/crates/doc/kani/futures/fn.yield_now.html new file mode 100644 index 000000000000..207bfa170dde --- /dev/null +++ b/crates/doc/kani/futures/fn.yield_now.html @@ -0,0 +1,3 @@ +yield_now in kani::futures - Rust

Function yield_now

Source
pub fn yield_now() -> impl Future<Output = ()>
Expand description

Suspends execution of the current future, to allow the scheduler to poll another future

+

Specifically, it returns a future that isn’t ready until the second time it is polled.

+
\ No newline at end of file diff --git a/crates/doc/kani/futures/index.html b/crates/doc/kani/futures/index.html new file mode 100644 index 000000000000..b11591f8e59f --- /dev/null +++ b/crates/doc/kani/futures/index.html @@ -0,0 +1,2 @@ +kani::futures - Rust

Module futures

Source
Expand description

This module contains functions to work with futures (and async/.await) in Kani.

+

Structs§

JoinHandle
Result of spawning a task.
RoundRobin
Keeps cycling through the tasks in a deterministic order

Enums§

SchedulingAssumption
Indicates to the scheduler whether it can kani::assume that the returned task is running.

Traits§

SchedulingStrategy
Trait that determines the possible sequence of tasks scheduling for a harness.

Functions§

block_on
A very simple executor: it polls the future in a busy loop until completion
block_on_with_spawn
Polls the given future and the tasks it may spawn until all of them complete
spawn
Spawns a task on the current global executor (which is set by block_on_with_spawn)
yield_now
Suspends execution of the current future, to allow the scheduler to poll another future
\ No newline at end of file diff --git a/crates/doc/kani/futures/sidebar-items.js b/crates/doc/kani/futures/sidebar-items.js new file mode 100644 index 000000000000..e59286f747cf --- /dev/null +++ b/crates/doc/kani/futures/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["SchedulingAssumption"],"fn":["block_on","block_on_with_spawn","spawn","yield_now"],"struct":["JoinHandle","RoundRobin"],"trait":["SchedulingStrategy"]}; \ No newline at end of file diff --git a/crates/doc/kani/futures/struct.JoinHandle.html b/crates/doc/kani/futures/struct.JoinHandle.html new file mode 100644 index 000000000000..5984e679c4ab --- /dev/null +++ b/crates/doc/kani/futures/struct.JoinHandle.html @@ -0,0 +1,15 @@ +JoinHandle in kani::futures - Rust

Struct JoinHandle

Source
pub struct JoinHandle { /* private fields */ }
Expand description

Result of spawning a task.

+

If you .await a JoinHandle, this will wait for the spawned task to complete.

+

Trait Implementations§

Source§

impl Future for JoinHandle

Source§

type Output = ()

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/crates/doc/kani/futures/struct.RoundRobin.html b/crates/doc/kani/futures/struct.RoundRobin.html new file mode 100644 index 000000000000..316aad17ec32 --- /dev/null +++ b/crates/doc/kani/futures/struct.RoundRobin.html @@ -0,0 +1,12 @@ +RoundRobin in kani::futures - Rust

Struct RoundRobin

Source
pub struct RoundRobin { /* private fields */ }
Expand description

Keeps cycling through the tasks in a deterministic order

+

Trait Implementations§

Source§

impl Default for RoundRobin

Source§

fn default() -> RoundRobin

Returns the “default value” for a type. Read more
Source§

impl SchedulingStrategy for RoundRobin

Source§

fn pick_task(&mut self, num_tasks: usize) -> (usize, SchedulingAssumption)

Picks the next task to be scheduled whenever the scheduler needs to pick a task to run next, and whether it can be assumed that the picked task is still running Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/crates/doc/kani/futures/trait.SchedulingStrategy.html b/crates/doc/kani/futures/trait.SchedulingStrategy.html new file mode 100644 index 000000000000..903fb299dda7 --- /dev/null +++ b/crates/doc/kani/futures/trait.SchedulingStrategy.html @@ -0,0 +1,25 @@ +SchedulingStrategy in kani::futures - Rust

Trait SchedulingStrategy

Source
pub trait SchedulingStrategy {
+    // Required method
+    fn pick_task(&mut self, num_tasks: usize) -> (usize, SchedulingAssumption);
+}
Expand description

Trait that determines the possible sequence of tasks scheduling for a harness.

+

If your harness spawns several tasks, Kani’s scheduler has to decide in what order to poll them. +This order may depend on the needs of your verification goal. +For example, you sometimes may wish to verify all possible schedulings, i.e. a nondeterministic scheduling strategy.

+

Nondeterministic scheduling strategies can be very slow to verify because they require Kani to check a large number of permutations of tasks. +So if you want to verify a harness that uses spawn, but don’t care about concurrency issues, you can simply use a deterministic scheduling strategy, +such as RoundRobin, which polls each task in turn.

+

Finally, you have the option of providing your own scheduling strategy by implementing this trait. +This can be useful, for example, if you want to verify that things work correctly for a very specific task ordering.

+

Required Methods§

Source

fn pick_task(&mut self, num_tasks: usize) -> (usize, SchedulingAssumption)

Picks the next task to be scheduled whenever the scheduler needs to pick a task to run next, and whether it can be assumed that the picked task is still running

+

Tasks are numbered 0..num_tasks. +For example, if pick_task(4) returns (2, CanAssumeRunning) than it picked the task with index 2 and allows Kani to assume that this task is still running. +This is useful if the task is chosen nondeterministicall (kani::any()) and allows the verifier to discard useless execution branches (such as polling a completed task again).

+

As a rule of thumb: +if the scheduling strategy picks the next task nondeterministically (using kani::any()), return CanAssumeRunning, otherwise CannotAssumeRunning. +When returning CanAssumeRunning, the scheduler will then assume that the picked task is still running, which cuts off “useless” paths where a completed task is polled again. +It is even necessary to make things terminate if nondeterminism is involved: +if we pick the task nondeterministically, and don’t have the restriction to still running tasks, we could poll the same task over and over again.

+

However, for most deterministic scheduling strategies, e.g. the round robin scheduling strategy, assuming that the picked task is still running is generally not possible +because if that task has ended, we are saying assume(false) and the verification effectively stops (which is undesirable, of course). +In such cases, return CannotAssumeRunning instead.

+

Implementors§

\ No newline at end of file diff --git a/crates/doc/kani/index.html b/crates/doc/kani/index.html new file mode 100644 index 000000000000..9c6c24f46277 --- /dev/null +++ b/crates/doc/kani/index.html @@ -0,0 +1,22 @@ +kani - Rust

Crate kani

Source

Re-exports§

pub use invariant::Invariant;
pub use futures::RoundRobin;
pub use futures::block_on;
pub use futures::block_on_with_spawn;
pub use futures::spawn;
pub use futures::yield_now;

Modules§

arbitrary
This module introduces the Arbitrary trait as well as implementation for +primitive types and other std containers.
contracts
Kani implementation of function contracts.
float
This module contains functions useful for float-related checks
futures
This module contains functions to work with futures (and async/.await) in Kani.
invariant
This module introduces the Invariant trait as well as its implementation +for primitive types.
mem
This module contains functions useful for checking unsafe memory access.
shadow
This module contains an API for shadow memory. +Shadow memory is a mechanism by which we can store metadata on memory +locations, e.g. whether a memory location is initialized.
slice
vec

Macros§

arbitrary_tuple
This macro implements kani::Arbitrary on a tuple whose elements +already implement kani::Arbitrary by running kani::any() on +each index of the tuple.
cover
generate_arbitrary
generate_float
generate_models
implies
implies!(premise => conclusion) means that if the premise is true, so +must be the conclusion.
kani_intrinsics
Kani intrinsics contains the public APIs used by users to verify their harnesses. +This macro is a part of kani_core as that allows us to verify even libraries that are no_core +such as core in rust’s std library itself.
kani_lib
Users should only need to invoke this.
kani_mem
kani_mem_init
ptr_generator
ptr_generator_fn
slice_generator

Structs§

ArbitraryPointer
Holds information about a pointer that is generated non-deterministically.
PointerGenerator
Pointer generator that can be used to generate arbitrary pointers.

Enums§

AllocationStatus
Enumeration with the cases currently covered by the pointer generator.

Traits§

Arbitrary

Functions§

any
This creates an symbolic valid value of type T. You can assign the return value of this +function to a variable that you want to make symbolic.
any_where
This creates a symbolic valid value of type T. +The value is constrained to be a value accepted by the predicate passed to the filter. +You can assign the return value of this function to a variable that you want to make symbolic.
assert
Creates an assertion of the specified condition and message.
assume
Creates an assumption that will be valid after this statement run. Note that the assumption +will only be applied for paths that follow the assumption. If the assumption doesn’t hold, the +program will exit successfully.
concrete_playback_run
NOP concrete_playback for type checking during verification mode.
cover
Creates a cover property with the specified condition and message.
pointer_generator
Create a pointer generator that fits at least N elements of type T.

Attribute Macros§

ensures
Add a postcondition to this function.
loop_invariant
Add a loop invariant to this loop.
modifies
Declaration of an explicit write-set for the annotated function.
proof
Marks a Kani proof harness
proof_for_contract
Designates this function as a harness to check a function contract.
recursion
Specifies that a function contains recursion for contract instrumentation.**
requires
Add a precondition to this function.
should_panic
Specifies that a proof harness is expected to panic.**
solver
Select the SAT solver to use with CBMC for this harness
stub
Specify a function/method stub pair to use for proof harness
stub_verified
stub_verified(TARGET) is a harness attribute (to be used on +proof or proof_for_contract +function) that replaces all occurrences of TARGET reachable from this +harness with a stub generated from the contract on TARGET.
unwind
Set Loop unwind limit for proof harnesses +The attribute #[kani::unwind(arg)] can only be called alongside #[kani::proof]. +arg - Takes in a integer value (u32) that represents the unwind value for the harness.

Derive Macros§

Arbitrary
Allow users to auto generate Arbitrary implementations by using +#[derive(Arbitrary)] macro.
Invariant
Allow users to auto generate Invariant implementations by using +#[derive(Invariant)] macro.
\ No newline at end of file diff --git a/crates/doc/kani/invariant/index.html b/crates/doc/kani/invariant/index.html new file mode 100644 index 000000000000..b88cba54f6bc --- /dev/null +++ b/crates/doc/kani/invariant/index.html @@ -0,0 +1,6 @@ +kani::invariant - Rust

Module invariant

Source
Expand description

This module introduces the Invariant trait as well as its implementation +for primitive types.

+

Traits§

Invariant
This trait should be used to specify and check type safety invariants for a +type. For type invariants, we refer to the definitions in the Rust’s Unsafe +Code Guidelines Reference: +https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#validity-and-safety-invariant
\ No newline at end of file diff --git a/crates/doc/kani/invariant/sidebar-items.js b/crates/doc/kani/invariant/sidebar-items.js new file mode 100644 index 000000000000..20c3006f84af --- /dev/null +++ b/crates/doc/kani/invariant/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"trait":["Invariant"]}; \ No newline at end of file diff --git a/crates/doc/kani/invariant/trait.Invariant.html b/crates/doc/kani/invariant/trait.Invariant.html new file mode 100644 index 000000000000..3f214a31f14c --- /dev/null +++ b/crates/doc/kani/invariant/trait.Invariant.html @@ -0,0 +1,53 @@ +Invariant in kani::invariant - Rust

Trait Invariant

Source
pub trait Invariant
where + Self: Sized,
{ + // Required method + fn is_safe(&self) -> bool; +}
Expand description

This trait should be used to specify and check type safety invariants for a +type. For type invariants, we refer to the definitions in the Rust’s Unsafe +Code Guidelines Reference: +https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#validity-and-safety-invariant

+

In summary, the reference distinguishes two kinds of type invariants:

+
    +
  • Validity invariant: An invariant that all data must uphold any time +it’s accessed or copied in a typed manner. This invariant is exploited by +the compiler to perform optimizations.
  • +
  • Safety invariant: An invariant that safe code may assume all data to +uphold. This invariant can be temporarily violated by unsafe code, but +must always be upheld when interfacing with unknown safe code.
  • +
+

Therefore, validity invariants must be upheld at all times, while safety +invariants only need to be upheld at the boundaries to safe code.

+

Safety invariants are particularly interesting for user-defined types, and +the Invariant trait allows you to check them with Kani.

+

It can also be used in tests. It’s a programmatic way to specify (in Rust) +properties over your data types. Since it’s written in Rust, it can be used +for static and dynamic checking.

+

For example, let’s say you’re creating a type that represents a date:

+ +
#[derive(kani::Arbitrary)]
+pub struct MyDate {
+  day: u8,
+  month: u8,
+  year: i64,
+}
+

You can specify its safety invariant as:

+ +

+impl kani::Invariant for MyDate {
+  fn is_safe(&self) -> bool {
+    self.month > 0
+      && self.month <= 12
+      && self.day > 0
+      && self.day <= days_in_month(self.year, self.month)
+  }
+}
+

And use it to check that your APIs are safe:

+ +
#[kani::proof]
+fn check_increase_date() {
+  let mut date: MyDate = kani::any();
+  // Increase date by one day
+  increase_date(&mut date, 1);
+  assert!(date.is_safe());
+}
+

Required Methods§

Source

fn is_safe(&self) -> bool

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Invariant for bool

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for char

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for f16

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for f32

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for f64

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for f128

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for i8

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for i16

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for i32

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for i64

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for i128

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for isize

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for u8

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for u16

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for u32

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for u64

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for u128

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for ()

Source§

fn is_safe(&self) -> bool

Source§

impl Invariant for usize

Source§

fn is_safe(&self) -> bool

Implementors§

\ No newline at end of file diff --git a/crates/doc/kani/macro.arbitrary_tuple!.html b/crates/doc/kani/macro.arbitrary_tuple!.html new file mode 100644 index 000000000000..41d78d6843f2 --- /dev/null +++ b/crates/doc/kani/macro.arbitrary_tuple!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.arbitrary_tuple.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.arbitrary_tuple.html b/crates/doc/kani/macro.arbitrary_tuple.html new file mode 100644 index 000000000000..03fc08793c29 --- /dev/null +++ b/crates/doc/kani/macro.arbitrary_tuple.html @@ -0,0 +1,6 @@ +arbitrary_tuple in kani - Rust

Macro arbitrary_tuple

macro_rules! arbitrary_tuple {
+    ($($type:ident),*) => { ... };
+}
Expand description

This macro implements kani::Arbitrary on a tuple whose elements +already implement kani::Arbitrary by running kani::any() on +each index of the tuple.

+
\ No newline at end of file diff --git a/crates/doc/kani/macro.cover!.html b/crates/doc/kani/macro.cover!.html new file mode 100644 index 000000000000..c9d65d137dea --- /dev/null +++ b/crates/doc/kani/macro.cover!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.cover.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.cover.html b/crates/doc/kani/macro.cover.html new file mode 100644 index 000000000000..b5214bb326d4 --- /dev/null +++ b/crates/doc/kani/macro.cover.html @@ -0,0 +1,5 @@ +cover in kani - Rust

Macro cover

Source
macro_rules! cover {
+    () => { ... };
+    ($cond:expr $(,)?) => { ... };
+    ($cond:expr, $msg:literal) => { ... };
+}
\ No newline at end of file diff --git a/crates/doc/kani/macro.generate_arbitrary!.html b/crates/doc/kani/macro.generate_arbitrary!.html new file mode 100644 index 000000000000..98cfe2e40bd1 --- /dev/null +++ b/crates/doc/kani/macro.generate_arbitrary!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.generate_arbitrary.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.generate_arbitrary.html b/crates/doc/kani/macro.generate_arbitrary.html new file mode 100644 index 000000000000..d92cf9b355af --- /dev/null +++ b/crates/doc/kani/macro.generate_arbitrary.html @@ -0,0 +1,3 @@ +generate_arbitrary in kani - Rust

Macro generate_arbitrary

macro_rules! generate_arbitrary {
+    ($core:path) => { ... };
+}
\ No newline at end of file diff --git a/crates/doc/kani/macro.generate_float!.html b/crates/doc/kani/macro.generate_float!.html new file mode 100644 index 000000000000..523dbb8227ec --- /dev/null +++ b/crates/doc/kani/macro.generate_float!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.generate_float.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.generate_float.html b/crates/doc/kani/macro.generate_float.html new file mode 100644 index 000000000000..af361d5dc643 --- /dev/null +++ b/crates/doc/kani/macro.generate_float.html @@ -0,0 +1,3 @@ +generate_float in kani - Rust

Macro generate_float

macro_rules! generate_float {
+    ($core:path) => { ... };
+}
\ No newline at end of file diff --git a/crates/doc/kani/macro.generate_models!.html b/crates/doc/kani/macro.generate_models!.html new file mode 100644 index 000000000000..87ba6c218af6 --- /dev/null +++ b/crates/doc/kani/macro.generate_models!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.generate_models.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.generate_models.html b/crates/doc/kani/macro.generate_models.html new file mode 100644 index 000000000000..8014a317946c --- /dev/null +++ b/crates/doc/kani/macro.generate_models.html @@ -0,0 +1,3 @@ +generate_models in kani - Rust

Macro generate_models

macro_rules! generate_models {
+    () => { ... };
+}
\ No newline at end of file diff --git a/crates/doc/kani/macro.implies!.html b/crates/doc/kani/macro.implies!.html new file mode 100644 index 000000000000..259c42463e06 --- /dev/null +++ b/crates/doc/kani/macro.implies!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.implies.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.implies.html b/crates/doc/kani/macro.implies.html new file mode 100644 index 000000000000..aef26f57a275 --- /dev/null +++ b/crates/doc/kani/macro.implies.html @@ -0,0 +1,7 @@ +implies in kani - Rust

Macro implies

Source
macro_rules! implies {
+    ($premise:expr => $conclusion:expr) => { ... };
+}
Expand description

implies!(premise => conclusion) means that if the premise is true, so +must be the conclusion.

+

This simply expands to !premise || conclusion and is intended to make checks more readable, +as the concept of an implication is more natural to think about than its expansion.

+
\ No newline at end of file diff --git a/crates/doc/kani/macro.kani_intrinsics!.html b/crates/doc/kani/macro.kani_intrinsics!.html new file mode 100644 index 000000000000..a289210142ff --- /dev/null +++ b/crates/doc/kani/macro.kani_intrinsics!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.kani_intrinsics.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.kani_intrinsics.html b/crates/doc/kani/macro.kani_intrinsics.html new file mode 100644 index 000000000000..1596a0bd9a94 --- /dev/null +++ b/crates/doc/kani/macro.kani_intrinsics.html @@ -0,0 +1,7 @@ +kani_intrinsics in kani - Rust

Macro kani_intrinsics

macro_rules! kani_intrinsics {
+    ($core:tt) => { ... };
+}
Expand description

Kani intrinsics contains the public APIs used by users to verify their harnesses. +This macro is a part of kani_core as that allows us to verify even libraries that are no_core +such as core in rust’s std library itself.

+

TODO: Use this inside kani library so that we dont have to maintain two copies of the same intrinsics.

+
\ No newline at end of file diff --git a/crates/doc/kani/macro.kani_lib!.html b/crates/doc/kani/macro.kani_lib!.html new file mode 100644 index 000000000000..b5fec7073828 --- /dev/null +++ b/crates/doc/kani/macro.kani_lib!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.kani_lib.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.kani_lib.html b/crates/doc/kani/macro.kani_lib.html new file mode 100644 index 000000000000..3503db606486 --- /dev/null +++ b/crates/doc/kani/macro.kani_lib.html @@ -0,0 +1,11 @@ +kani_lib in kani - Rust

Macro kani_lib

macro_rules! kani_lib {
+    (core) => { ... };
+    (kani) => { ... };
+}
Expand description

Users should only need to invoke this.

+

Options are:

+
    +
  • kani: Add definitions needed for Kani library.
  • +
  • core: Define a kani module inside core crate.
  • +
  • std: TODO: Define a kani module inside std crate. Users must define kani inside core.
  • +
+
\ No newline at end of file diff --git a/crates/doc/kani/macro.kani_mem!.html b/crates/doc/kani/macro.kani_mem!.html new file mode 100644 index 000000000000..c97a4e42f782 --- /dev/null +++ b/crates/doc/kani/macro.kani_mem!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.kani_mem.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.kani_mem.html b/crates/doc/kani/macro.kani_mem.html new file mode 100644 index 000000000000..b53027256a63 --- /dev/null +++ b/crates/doc/kani/macro.kani_mem.html @@ -0,0 +1,3 @@ +kani_mem in kani - Rust

Macro kani_mem

macro_rules! kani_mem {
+    ($core:tt) => { ... };
+}
\ No newline at end of file diff --git a/crates/doc/kani/macro.kani_mem_init!.html b/crates/doc/kani/macro.kani_mem_init!.html new file mode 100644 index 000000000000..bc7bd48bace1 --- /dev/null +++ b/crates/doc/kani/macro.kani_mem_init!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.kani_mem_init.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.kani_mem_init.html b/crates/doc/kani/macro.kani_mem_init.html new file mode 100644 index 000000000000..dca5c196ba13 --- /dev/null +++ b/crates/doc/kani/macro.kani_mem_init.html @@ -0,0 +1,3 @@ +kani_mem_init in kani - Rust

Macro kani_mem_init

macro_rules! kani_mem_init {
+    ($core:path) => { ... };
+}
\ No newline at end of file diff --git a/crates/doc/kani/macro.ptr_generator!.html b/crates/doc/kani/macro.ptr_generator!.html new file mode 100644 index 000000000000..924ba7351c4c --- /dev/null +++ b/crates/doc/kani/macro.ptr_generator!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.ptr_generator.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.ptr_generator.html b/crates/doc/kani/macro.ptr_generator.html new file mode 100644 index 000000000000..44b79927a9e2 --- /dev/null +++ b/crates/doc/kani/macro.ptr_generator.html @@ -0,0 +1,3 @@ +ptr_generator in kani - Rust

Macro ptr_generator

macro_rules! ptr_generator {
+    () => { ... };
+}
\ No newline at end of file diff --git a/crates/doc/kani/macro.ptr_generator_fn!.html b/crates/doc/kani/macro.ptr_generator_fn!.html new file mode 100644 index 000000000000..6be49540f25e --- /dev/null +++ b/crates/doc/kani/macro.ptr_generator_fn!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.ptr_generator_fn.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.ptr_generator_fn.html b/crates/doc/kani/macro.ptr_generator_fn.html new file mode 100644 index 000000000000..783616112076 --- /dev/null +++ b/crates/doc/kani/macro.ptr_generator_fn.html @@ -0,0 +1,3 @@ +ptr_generator_fn in kani - Rust

Macro ptr_generator_fn

macro_rules! ptr_generator_fn {
+    () => { ... };
+}
\ No newline at end of file diff --git a/crates/doc/kani/macro.slice_generator!.html b/crates/doc/kani/macro.slice_generator!.html new file mode 100644 index 000000000000..c2ed3ec934b3 --- /dev/null +++ b/crates/doc/kani/macro.slice_generator!.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to macro.slice_generator.html...

+ + + \ No newline at end of file diff --git a/crates/doc/kani/macro.slice_generator.html b/crates/doc/kani/macro.slice_generator.html new file mode 100644 index 000000000000..5503043d9ac6 --- /dev/null +++ b/crates/doc/kani/macro.slice_generator.html @@ -0,0 +1,3 @@ +slice_generator in kani - Rust

Macro slice_generator

macro_rules! slice_generator {
+    () => { ... };
+}
\ No newline at end of file diff --git a/crates/doc/kani/mem/fn.can_dereference.html b/crates/doc/kani/mem/fn.can_dereference.html new file mode 100644 index 000000000000..0727a41f2774 --- /dev/null +++ b/crates/doc/kani/mem/fn.can_dereference.html @@ -0,0 +1,10 @@ +can_dereference in kani::mem - Rust

Function can_dereference

Source
pub fn can_dereference<T: ?Sized>(ptr: *const T) -> bool
Expand description

Checks that pointer ptr point to a valid value of type T.

+

For that, the pointer has to be a valid pointer according to crate::mem conditions 1, 2 +and 3, +and the value stored must respect the validity invariants for type T.

+

TODO: Kani should automatically add those checks when a de-reference happens. +https://github.com/model-checking/kani/issues/2975

+

This function will panic today if the pointer is not null, and it points to an unallocated or +deallocated memory location. This is an existing Kani limitation. +See https://github.com/model-checking/kani/issues/2690 for more details.

+
\ No newline at end of file diff --git a/crates/doc/kani/mem/fn.can_read_unaligned.html b/crates/doc/kani/mem/fn.can_read_unaligned.html new file mode 100644 index 000000000000..a04f1a7060a2 --- /dev/null +++ b/crates/doc/kani/mem/fn.can_read_unaligned.html @@ -0,0 +1,10 @@ +can_read_unaligned in kani::mem - Rust

Function can_read_unaligned

Source
pub fn can_read_unaligned<T: ?Sized>(ptr: *const T) -> bool
Expand description

Checks that pointer ptr point to a valid value of type T.

+

For that, the pointer has to be a valid pointer according to crate::mem conditions 1, 2 +and 3, +and the value stored must respect the validity invariants for type T.

+

Note this function succeeds for unaligned pointers. See self::can_dereference if you also +want to check pointer alignment.

+

This function will panic today if the pointer is not null, and it points to an unallocated or +deallocated memory location. This is an existing Kani limitation. +See https://github.com/model-checking/kani/issues/2690 for more details.

+
\ No newline at end of file diff --git a/crates/doc/kani/mem/fn.can_write.html b/crates/doc/kani/mem/fn.can_write.html new file mode 100644 index 000000000000..d5dd4da06571 --- /dev/null +++ b/crates/doc/kani/mem/fn.can_write.html @@ -0,0 +1,10 @@ +can_write in kani::mem - Rust

Function can_write

Source
pub fn can_write<T: ?Sized>(ptr: *mut T) -> bool
Expand description

Check if the pointer is valid for write access according to crate::mem conditions 1, 2 +and 3.

+

Note this function also checks for pointer alignment. Use self::can_write_unaligned +if you don’t want to fail for unaligned pointers.

+

This function does not check if the value stored is valid for the given type. Use +self::can_dereference for that.

+

This function will panic today if the pointer is not null, and it points to an unallocated or +deallocated memory location. This is an existing Kani limitation. +See https://github.com/model-checking/kani/issues/2690 for more details.

+
\ No newline at end of file diff --git a/crates/doc/kani/mem/fn.can_write_unaligned.html b/crates/doc/kani/mem/fn.can_write_unaligned.html new file mode 100644 index 000000000000..18f49529cc1f --- /dev/null +++ b/crates/doc/kani/mem/fn.can_write_unaligned.html @@ -0,0 +1,8 @@ +can_write_unaligned in kani::mem - Rust

Function can_write_unaligned

Source
pub fn can_write_unaligned<T: ?Sized>(ptr: *const T) -> bool
Expand description

Check if the pointer is valid for unaligned write access according to crate::mem conditions +1, 2 and 3.

+

Note this function succeeds for unaligned pointers. See self::can_write if you also +want to check pointer alignment.

+

This function will panic today if the pointer is not null, and it points to an unallocated or +deallocated memory location. This is an existing Kani limitation. +See https://github.com/model-checking/kani/issues/2690 for more details.

+
\ No newline at end of file diff --git a/crates/doc/kani/mem/fn.checked_align_of_raw.html b/crates/doc/kani/mem/fn.checked_align_of_raw.html new file mode 100644 index 000000000000..135c7254d89f --- /dev/null +++ b/crates/doc/kani/mem/fn.checked_align_of_raw.html @@ -0,0 +1,4 @@ +checked_align_of_raw in kani::mem - Rust

Function checked_align_of_raw

Source
pub fn checked_align_of_raw<T: ?Sized>(ptr: *const T) -> Option<usize>
Expand description

Compute the size of the val pointed to if safe.

+

Return None if alignment information cannot be retrieved (foreign types), or if value +is not power-of-two.

+
\ No newline at end of file diff --git a/crates/doc/kani/mem/fn.checked_size_of_raw.html b/crates/doc/kani/mem/fn.checked_size_of_raw.html new file mode 100644 index 000000000000..65a56413c244 --- /dev/null +++ b/crates/doc/kani/mem/fn.checked_size_of_raw.html @@ -0,0 +1,4 @@ +checked_size_of_raw in kani::mem - Rust

Function checked_size_of_raw

Source
pub fn checked_size_of_raw<T: ?Sized>(ptr: *const T) -> Option<usize>
Expand description

Compute the size of the val pointed to if it is safe to do so.

+

Return None if an overflow would occur, or if alignment is not power of two. +TODO: Optimize this if T is sized.

+
\ No newline at end of file diff --git a/crates/doc/kani/mem/fn.same_allocation.html b/crates/doc/kani/mem/fn.same_allocation.html new file mode 100644 index 000000000000..762e6c523696 --- /dev/null +++ b/crates/doc/kani/mem/fn.same_allocation.html @@ -0,0 +1,4 @@ +same_allocation in kani::mem - Rust

Function same_allocation

Source
pub fn same_allocation<T: ?Sized>(ptr1: *const T, ptr2: *const T) -> bool
Expand description

Check if two pointers points to the same allocated object, and that both pointers +are in bounds of that object.

+

A pointer is still considered in-bounds if it points to 1-byte past the allocation.

+
\ No newline at end of file diff --git a/crates/doc/kani/mem/index.html b/crates/doc/kani/mem/index.html new file mode 100644 index 000000000000..246e05fdf7c3 --- /dev/null +++ b/crates/doc/kani/mem/index.html @@ -0,0 +1,37 @@ +kani::mem - Rust

Module mem

Source
Expand description

This module contains functions useful for checking unsafe memory access.

+

Given the following validity rules provided in the Rust documentation: +https://doc.rust-lang.org/std/ptr/index.html (accessed Feb 6th, 2024)

+
    +
  1. A null pointer is never valid, not even for accesses of size zero.
  2. +
  3. For a pointer to be valid, it is necessary, but not always sufficient, that the pointer +be dereferenceable: the memory range of the given size starting at the pointer must all be +within the bounds of a single allocated object. Note that in Rust, every (stack-allocated) +variable is considered a separate allocated object. +Even for operations of size zero, the pointer must not be pointing to deallocated memory, +i.e., deallocation makes pointers invalid even for zero-sized operations. +ZST access is not OK for any pointer. +See: https://github.com/rust-lang/unsafe-code-guidelines/issues/472
  4. +
  5. However, casting any non-zero integer literal to a pointer is valid for zero-sized +accesses, even if some memory happens to exist at that address and gets deallocated. +This corresponds to writing your own allocator: allocating zero-sized objects is not very +hard. The canonical way to obtain a pointer that is valid for zero-sized accesses is +NonNull::dangling.
  6. +
  7. All accesses performed by functions in this module are non-atomic in the sense of atomic +operations used to synchronize between threads. +This means it is undefined behavior to perform two concurrent accesses to the same location +from different threads unless both accesses only read from memory. +Notice that this explicitly includes read_volatile and write_volatile: +Volatile accesses cannot be used for inter-thread synchronization.
  8. +
  9. The result of casting a reference to a pointer is valid for as long as the underlying +object is live and no reference (just raw pointers) is used to access the same memory. +That is, reference and pointer accesses cannot be interleaved.
  10. +
+

Kani is able to verify #1 and #2 today.

+

For #3, we are overly cautious, and Kani will only consider zero-sized pointer access safe if +the address matches NonNull::<()>::dangling(). +The way Kani tracks provenance is not enough to check if the address was the result of a cast +from a non-zero integer literal.

+

Functions§

can_dereference
Checks that pointer ptr point to a valid value of type T.
can_read_unaligned
Checks that pointer ptr point to a valid value of type T.
can_write
Check if the pointer is valid for write access according to crate::mem conditions 1, 2 +and 3.
can_write_unaligned
Check if the pointer is valid for unaligned write access according to crate::mem conditions +1, 2 and 3.
checked_align_of_raw
Compute the size of the val pointed to if safe.
checked_size_of_raw
Compute the size of the val pointed to if it is safe to do so.
same_allocation
Check if two pointers points to the same allocated object, and that both pointers +are in bounds of that object.
\ No newline at end of file diff --git a/crates/doc/kani/mem/sidebar-items.js b/crates/doc/kani/mem/sidebar-items.js new file mode 100644 index 000000000000..a3a862eba058 --- /dev/null +++ b/crates/doc/kani/mem/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["can_dereference","can_read_unaligned","can_write","can_write_unaligned","checked_align_of_raw","checked_size_of_raw","same_allocation"]}; \ No newline at end of file diff --git a/crates/doc/kani/shadow/index.html b/crates/doc/kani/shadow/index.html new file mode 100644 index 000000000000..3e96eb43973e --- /dev/null +++ b/crates/doc/kani/shadow/index.html @@ -0,0 +1,24 @@ +kani::shadow - Rust

Module shadow

Source
Expand description

This module contains an API for shadow memory. +Shadow memory is a mechanism by which we can store metadata on memory +locations, e.g. whether a memory location is initialized.

+

The main data structure provided by this module is the ShadowMem struct, +which allows us to store metadata on a given memory location.

+

§Example

+
use kani::shadow::ShadowMem;
+use std::alloc::{alloc, Layout};
+
+let mut sm = ShadowMem::new(false);
+
+unsafe {
+    let ptr = alloc(Layout::new::<u8>());
+    // assert the memory location is not initialized
+    assert!(!sm.get(ptr));
+    // write to the memory location
+    *ptr = 42;
+    // update the shadow memory to indicate that this location is now initialized
+    sm.set(ptr, true);
+}
+

Structs§

ShadowMem
A shadow memory data structure that contains a two-dimensional array of a +generic type T. +Each element of the outer array represents an object, and each element of +the inner array represents a byte in the object.
\ No newline at end of file diff --git a/crates/doc/kani/shadow/sidebar-items.js b/crates/doc/kani/shadow/sidebar-items.js new file mode 100644 index 000000000000..1975d4706304 --- /dev/null +++ b/crates/doc/kani/shadow/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["ShadowMem"]}; \ No newline at end of file diff --git a/crates/doc/kani/shadow/struct.ShadowMem.html b/crates/doc/kani/shadow/struct.ShadowMem.html new file mode 100644 index 000000000000..353eebcacefc --- /dev/null +++ b/crates/doc/kani/shadow/struct.ShadowMem.html @@ -0,0 +1,24 @@ +ShadowMem in kani::shadow - Rust

Struct ShadowMem

Source
pub struct ShadowMem<T: Copy> { /* private fields */ }
Expand description

A shadow memory data structure that contains a two-dimensional array of a +generic type T. +Each element of the outer array represents an object, and each element of +the inner array represents a byte in the object.

+

Implementations§

Source§

impl<T: Copy> ShadowMem<T>

Source

pub const fn new(val: T) -> Self

Create a new shadow memory instance initialized with the given value

+
Source

pub fn get<U>(&self, ptr: *const U) -> T

Get the shadow memory value of the given pointer

+
Source

pub fn set<U>(&mut self, ptr: *const U, val: T)

Set the shadow memory value of the given pointer

+

Auto Trait Implementations§

§

impl<T> Freeze for ShadowMem<T>
where + T: Freeze,

§

impl<T> RefUnwindSafe for ShadowMem<T>
where + T: RefUnwindSafe,

§

impl<T> Send for ShadowMem<T>
where + T: Send,

§

impl<T> Sync for ShadowMem<T>
where + T: Sync,

§

impl<T> Unpin for ShadowMem<T>
where + T: Unpin,

§

impl<T> UnwindSafe for ShadowMem<T>
where + T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/crates/doc/kani/sidebar-items.js b/crates/doc/kani/sidebar-items.js new file mode 100644 index 000000000000..ef8391620e6d --- /dev/null +++ b/crates/doc/kani/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"attr":["ensures","loop_invariant","modifies","proof","proof_for_contract","recursion","requires","should_panic","solver","stub","stub_verified","unwind"],"derive":["Arbitrary","Invariant"],"enum":["AllocationStatus"],"fn":["any","any_where","assert","assume","concrete_playback_run","cover","pointer_generator"],"macro":["arbitrary_tuple","cover","generate_arbitrary","generate_float","generate_models","implies","kani_intrinsics","kani_lib","kani_mem","kani_mem_init","ptr_generator","ptr_generator_fn","slice_generator"],"mod":["arbitrary","contracts","float","futures","invariant","mem","shadow","slice","vec"],"struct":["ArbitraryPointer","PointerGenerator"],"trait":["Arbitrary"]}; \ No newline at end of file diff --git a/crates/doc/kani/slice/fn.any_slice_of_array.html b/crates/doc/kani/slice/fn.any_slice_of_array.html new file mode 100644 index 000000000000..86225bf3bb41 --- /dev/null +++ b/crates/doc/kani/slice/fn.any_slice_of_array.html @@ -0,0 +1,9 @@ +any_slice_of_array in kani::slice - Rust

Function any_slice_of_array

Source
pub fn any_slice_of_array<T, const LENGTH: usize>(arr: &[T; LENGTH]) -> &[T]
Expand description

Given an array arr of length LENGTH, this function returns a valid +slice of arr with non-deterministic start and end points. This is useful +in situations where one wants to verify that all possible slices of a given +array satisfy some property.

+

§Example:

+
let arr = [1, 2, 3];
+let slice = kani::slice::any_slice_of_array(&arr);
+foo(slice); // where foo is a function that takes a slice and verifies a property about it
+
\ No newline at end of file diff --git a/crates/doc/kani/slice/fn.any_slice_of_array_mut.html b/crates/doc/kani/slice/fn.any_slice_of_array_mut.html new file mode 100644 index 000000000000..8cb5eecec071 --- /dev/null +++ b/crates/doc/kani/slice/fn.any_slice_of_array_mut.html @@ -0,0 +1,4 @@ +any_slice_of_array_mut in kani::slice - Rust

Function any_slice_of_array_mut

Source
pub fn any_slice_of_array_mut<T, const LENGTH: usize>(
+    arr: &mut [T; LENGTH],
+) -> &mut [T]
Expand description

A mutable version of the previous function

+
\ No newline at end of file diff --git a/crates/doc/kani/slice/index.html b/crates/doc/kani/slice/index.html new file mode 100644 index 000000000000..79601c2d9a51 --- /dev/null +++ b/crates/doc/kani/slice/index.html @@ -0,0 +1,4 @@ +kani::slice - Rust

Module slice

Source

Functions§

any_slice_of_array
Given an array arr of length LENGTH, this function returns a valid +slice of arr with non-deterministic start and end points. This is useful +in situations where one wants to verify that all possible slices of a given +array satisfy some property.
any_slice_of_array_mut
A mutable version of the previous function
\ No newline at end of file diff --git a/crates/doc/kani/slice/sidebar-items.js b/crates/doc/kani/slice/sidebar-items.js new file mode 100644 index 000000000000..f4af066c700e --- /dev/null +++ b/crates/doc/kani/slice/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["any_slice_of_array","any_slice_of_array_mut"]}; \ No newline at end of file diff --git a/crates/doc/kani/struct.ArbitraryPointer.html b/crates/doc/kani/struct.ArbitraryPointer.html new file mode 100644 index 000000000000..ffbe1fcd655c --- /dev/null +++ b/crates/doc/kani/struct.ArbitraryPointer.html @@ -0,0 +1,22 @@ +ArbitraryPointer in kani - Rust

Struct ArbitraryPointer

Source
pub struct ArbitraryPointer<'a, T> {
+    pub ptr: *mut T,
+    pub status: AllocationStatus,
+    pub is_initialized: bool,
+    /* private fields */
+}
Expand description

Holds information about a pointer that is generated non-deterministically.

+

Fields§

§ptr: *mut T

The pointer that was generated.

+
§status: AllocationStatus

The expected allocation status.

+
§is_initialized: bool

Whether the pointer was generated with an initialized value or not.

+

Trait Implementations§

Source§

impl<'a, T: Debug> Debug for ArbitraryPointer<'a, T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'a, T> Freeze for ArbitraryPointer<'a, T>

§

impl<'a, T> RefUnwindSafe for ArbitraryPointer<'a, T>
where + T: RefUnwindSafe,

§

impl<'a, T> !Send for ArbitraryPointer<'a, T>

§

impl<'a, T> !Sync for ArbitraryPointer<'a, T>

§

impl<'a, T> Unpin for ArbitraryPointer<'a, T>

§

impl<'a, T> UnwindSafe for ArbitraryPointer<'a, T>
where + T: RefUnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/crates/doc/kani/struct.PointerGenerator.html b/crates/doc/kani/struct.PointerGenerator.html new file mode 100644 index 000000000000..96ed5d3f754f --- /dev/null +++ b/crates/doc/kani/struct.PointerGenerator.html @@ -0,0 +1,149 @@ +PointerGenerator in kani - Rust

Struct PointerGenerator

Source
pub struct PointerGenerator<const BYTES: usize> { /* private fields */ }
Expand description

Pointer generator that can be used to generate arbitrary pointers.

+

This generator allows users to build pointers with different safety properties. +This is different than creating a pointer that can have any address, since it will never +point to a previously allocated object. +See this section +for more details.

+

The generator contains an internal buffer of a constant generic size, BYTES, that it +uses to generate InBounds and OutOfBounds pointers. +In those cases, the generated pointers will have the same provenance as the generator, +and the same lifetime. +The address of an InBounds pointer will represent all possible addresses in the range +of the generator’s buffer address.

+

For other allocation statuses, the generator will create a pointer that satisfies the +given condition. +The pointer address will not represent all possible addresses that satisfies the +given allocation status.

+

For example:

+ +
    let mut generator = PointerGenerator::<10>::new();
+    let arbitrary = generator.any_alloc_status::<char>();
+    kani::assume(arbitrary.status == AllocationStatus::InBounds);
+    // Pointer may be unaligned, but it should be in-bounds, so it is safe to write to
+    unsafe { arbitrary.ptr.write_unaligned(kani::any()) }
+

The generator is parameterized by the number of bytes of its internal buffer. +See pointer_generator function if you would like to create a generator that fits +a minimum number of objects of a given type. Example:

+ +
    // These generators have the same capacity of 6 bytes.
+    let generator1 = PointerGenerator::<6>::new();
+    let generator2 = pointer_generator::<i16, 3>();
+

§Buffer size

+

The internal buffer is used to generate pointers, and its size determines the maximum +number of pointers it can generate without overlapping. +Larger values will impact the maximum distance between generated pointers.

+

We recommend that you pick a size that is at least big enough to +cover the cases where all pointers produced are non-overlapping. +The buffer size in bytes must be big enough to fit distinct objects for each call +of generate pointer. +For example, generating two *mut u8 and one *mut u32 requires a buffer +of at least 6 bytes.

+

This guarantees that your harness covers cases where all generated pointers +point to allocated positions that do not overlap. For example:

+ +
    let mut generator = PointerGenerator::<6>::new();
+    let ptr1: *mut u8 = generator.any_in_bounds().ptr;
+    let ptr2: *mut u8 = generator.any_in_bounds().ptr;
+    let ptr3: *mut u32 = generator.any_in_bounds().ptr;
+    // This cover is satisfied.
+    cover!((ptr1 as usize) >= (ptr2 as usize) + size_of::<u8>()
+           && (ptr2 as usize) >= (ptr3 as usize) + size_of::<u32>());
+    // As well as having overlapping pointers.
+    cover!((ptr1 as usize) == (ptr3 as usize));
+

The first cover will be satisfied, since there exists at least one path where +the generator produces inbounds pointers that do not overlap. Such as this scenario:

+
+--------+--------+--------+--------+--------+--------+
+| Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 |
++--------+--------+--------+--------+--------+--------+
+<--------------- ptr3 --------------><--ptr2-><--ptr1->
+

I.e., the generator buffer is large enough to fit all 3 objects without overlapping.

+

In contrast, if we had used a size of 1 element, all calls to any_in_bounds() would +return elements that overlap, and the first cover would no longer be satisfied.

+

Note that the generator requires a minimum number of 1 byte, otherwise the +InBounds case would never be covered. +Compilation will fail if you try to create a generator of size 0.

+

Additionally, the verification will fail if you try to generate a pointer for a type +with size greater than the buffer size.

+

Use larger buffer size if you want to cover scenarios where the distance +between the generated pointers matters.

+

The only caveats of using very large numbers are:

+
    +
  1. The value cannot exceed the solver maximum object size (currently 2^48 by default), neither Rust’s +maximum object size (isize::MAX).
  2. +
  3. Larger sizes could impact performance as they can lead to an exponential increase in the number of possibilities of pointer placement within the buffer.
  4. +
+

§Pointer provenance

+

The pointer returned in the InBounds and OutOfBounds case will have the same +provenance as the generator.

+

Use the same generator if you want to handle cases where 2 or more pointers may overlap. E.g.:

+ +
    let mut generator = pointer_generator::<char, 5>();
+    let ptr1 = generator.any_in_bounds::<char>().ptr;
+    let ptr2 = generator.any_in_bounds::<char>().ptr;
+    // This cover is satisfied.
+    cover!(ptr1 == ptr2)
+

If you want to cover cases where two or more pointers may not have the same +provenance, you will need to instantiate multiple generators. +You can also apply non-determinism to cover cases where the pointers may or may not +have the same provenance. E.g.:

+ +
    let mut generator1 = pointer_generator::<char, 5>();
+    let mut generator2 = pointer_generator::<char, 5>();
+    let ptr1: *const char = generator1.any_in_bounds().ptr;
+    let ptr2: *const char = if kani::any() {
+        // Pointers will have same provenance and may overlap.
+        generator1.any_in_bounds().ptr
+    } else {
+        // Pointers will have different provenance and will not overlap.
+        generator2.any_in_bounds().ptr
+    };
+    // Invoke the function under verification
+    unsafe { my_target(ptr1, ptr2) };
+

§Pointer Generator vs Pointer with any address

+

Creating a pointer using the generator is different than generating a pointer +with any address.

+

I.e.:

+ +
    // This pointer represents any address, and it may point to anything in memory,
+    // allocated or not.
+    let ptr1 = kani::any::<usize>() as *const u8;
+
+    // This pointer address will either point to unallocated memory, to a dead object
+    // or to allocated memory within the generator address space.
+    let mut generator = PointerGenerator::<5>::new();
+    let ptr2: *const u8 = generator.any_alloc_status().ptr;
+

Kani cannot reason about a pointer allocation status (except for asserting its validity). +Thus, the generator was introduced to help writing harnesses that need to impose +constraints to the arbitrary pointer allocation status. +It also allow us to restrict the pointer provenance, excluding for example the address of +variables that are not available in the current context. +As a limitation, it will not cover the entire address space that a pointer can take.

+

If your harness does not need to reason about pointer allocation, for example, verifying +pointer wrapping arithmetic, using a pointer with any address will allow you to cover +all possible scenarios.

+

Implementations§

Source§

impl<const BYTES: usize> PointerGenerator<BYTES>

Source

pub fn new() -> Self

Create a new PointerGenerator.

+
Source

pub fn any_alloc_status<'a, T>(&'a mut self) -> ArbitraryPointer<'a, T>
where + T: Arbitrary,

Creates a raw pointer with non-deterministic properties.

+

The pointer returned is either dangling or has the same provenance of the generator.

+
Source

pub fn any_in_bounds<'a, T>(&'a mut self) -> ArbitraryPointer<'a, T>
where + T: Arbitrary,

Creates a in-bounds raw pointer with non-deterministic properties.

+

The pointer points to an allocated location with the same provenance of the generator. +The pointer may be unaligned, and the pointee may be uninitialized.

+ +
    let mut generator = PointerGenerator::<6>::new();
+    let ptr1: *mut u8 = generator.any_in_bounds().ptr;
+    let ptr2: *mut u8 = generator.any_in_bounds().ptr;
+    // SAFETY: Both pointers have the same provenance.
+    let distance = unsafe { ptr1.offset_from(ptr2) };
+    assert!(distance > -5 && distance < 5)
+

Trait Implementations§

Source§

impl<const BYTES: usize> Debug for PointerGenerator<BYTES>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<const BYTES: usize> Freeze for PointerGenerator<BYTES>

§

impl<const BYTES: usize> RefUnwindSafe for PointerGenerator<BYTES>

§

impl<const BYTES: usize> Send for PointerGenerator<BYTES>

§

impl<const BYTES: usize> Sync for PointerGenerator<BYTES>

§

impl<const BYTES: usize> Unpin for PointerGenerator<BYTES>

§

impl<const BYTES: usize> UnwindSafe for PointerGenerator<BYTES>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/crates/doc/kani/trait.Arbitrary.html b/crates/doc/kani/trait.Arbitrary.html new file mode 100644 index 000000000000..923491f9cfd3 --- /dev/null +++ b/crates/doc/kani/trait.Arbitrary.html @@ -0,0 +1,22 @@ +Arbitrary in kani - Rust

Trait Arbitrary

Source
pub trait Arbitrary
where + Self: Sized,
{ + // Required method + fn any() -> Self; + + // Provided method + fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH] { ... } +}

Required Methods§

Source

fn any() -> Self

Provided Methods§

Source

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Arbitrary for bool

Source§

fn any() -> Self

Source§

impl Arbitrary for char

Validate that a char is not outside the ranges [0x0, 0xD7FF] and [0xE000, 0x10FFFF] +Ref: https://doc.rust-lang.org/stable/nomicon/what-unsafe-does.html

+
Source§

fn any() -> Self

Source§

impl Arbitrary for f16

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for f32

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for f64

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for f128

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for i8

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for i16

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for i32

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for i64

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for i128

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for isize

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for u8

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for u16

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for u32

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for u64

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for u128

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for ()

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for usize

Source§

fn any() -> Self

Source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]

Source§

impl Arbitrary for PhantomPinned

Source§

fn any() -> Self

Source§

impl Arbitrary for Duration

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroI8

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroI16

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroI32

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroI64

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroI128

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroIsize

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroU8

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroU16

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroU32

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroU64

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroU128

Source§

fn any() -> Self

Source§

impl Arbitrary for NonZeroUsize

Source§

fn any() -> Self

Source§

impl<A: Arbitrary> Arbitrary for (A,)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary> Arbitrary for (A, B)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary> Arbitrary for (A, B, C)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary> Arbitrary for (A, B, C, D)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary> Arbitrary for (A, B, C, D, E)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary> Arbitrary for (A, B, C, D, E, F)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary> Arbitrary for (A, B, C, D, E, F, G)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary> Arbitrary for (A, B, C, D, E, F, G, H)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary, I: Arbitrary> Arbitrary for (A, B, C, D, E, F, G, H, I)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary, I: Arbitrary, J: Arbitrary> Arbitrary for (A, B, C, D, E, F, G, H, I, J)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary, I: Arbitrary, J: Arbitrary, K: Arbitrary> Arbitrary for (A, B, C, D, E, F, G, H, I, J, K)

Source§

fn any() -> Self

Source§

impl<A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary, I: Arbitrary, J: Arbitrary, K: Arbitrary, L: Arbitrary> Arbitrary for (A, B, C, D, E, F, G, H, I, J, K, L)

Source§

fn any() -> Self

Source§

impl<T> Arbitrary for Bound<T>
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T> Arbitrary for Option<T>
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T> Arbitrary for Box<T>
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T> Arbitrary for Range<T>
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T> Arbitrary for RangeFrom<T>
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T> Arbitrary for RangeInclusive<T>
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T> Arbitrary for RangeTo<T>
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T> Arbitrary for RangeToInclusive<T>
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T> Arbitrary for MaybeUninit<T>
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T, E> Arbitrary for Result<T, E>
where + T: Arbitrary, + E: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T, const N: usize> Arbitrary for [T; N]
where + T: Arbitrary,

Source§

fn any() -> Self

Source§

impl<T: ?Sized> Arbitrary for PhantomData<T>

Source§

fn any() -> Self

Implementors§

\ No newline at end of file diff --git a/crates/doc/kani/vec/fn.any_vec.html b/crates/doc/kani/vec/fn.any_vec.html new file mode 100644 index 000000000000..678630278255 --- /dev/null +++ b/crates/doc/kani/vec/fn.any_vec.html @@ -0,0 +1,3 @@ +any_vec in kani::vec - Rust

Function any_vec

Source
pub fn any_vec<T, const MAX_LENGTH: usize>() -> Vec<T>
where + T: Arbitrary,
Expand description

Generates an arbitrary vector whose length is at most MAX_LENGTH.

+
\ No newline at end of file diff --git a/crates/doc/kani/vec/fn.exact_vec.html b/crates/doc/kani/vec/fn.exact_vec.html new file mode 100644 index 000000000000..7e7b8033f7f2 --- /dev/null +++ b/crates/doc/kani/vec/fn.exact_vec.html @@ -0,0 +1,3 @@ +exact_vec in kani::vec - Rust

Function exact_vec

Source
pub fn exact_vec<T, const EXACT_LENGTH: usize>() -> Vec<T>
where + T: Arbitrary,
Expand description

Generates an arbitrary vector that is exactly EXACT_LENGTH long.

+
\ No newline at end of file diff --git a/crates/doc/kani/vec/index.html b/crates/doc/kani/vec/index.html new file mode 100644 index 000000000000..86848edf177c --- /dev/null +++ b/crates/doc/kani/vec/index.html @@ -0,0 +1 @@ +kani::vec - Rust

Module vec

Source

Functions§

any_vec
Generates an arbitrary vector whose length is at most MAX_LENGTH.
exact_vec
Generates an arbitrary vector that is exactly EXACT_LENGTH long.
\ No newline at end of file diff --git a/crates/doc/kani/vec/sidebar-items.js b/crates/doc/kani/vec/sidebar-items.js new file mode 100644 index 000000000000..1053d4559d72 --- /dev/null +++ b/crates/doc/kani/vec/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["any_vec","exact_vec"]}; \ No newline at end of file diff --git a/crates/doc/search-index.js b/crates/doc/search-index.js new file mode 100644 index 000000000000..ff6625ece82c --- /dev/null +++ b/crates/doc/search-index.js @@ -0,0 +1,4 @@ +var searchIndex = new Map(JSON.parse('[["kani",{"t":"GKYFPPPEYPPFEHMNNNNHCQHHEENNNNNNNNNHCHQXNCNNNNNNCQQQQNNNCOQQQQXCXNHXXOQQXXCXCQXEOXXNNNNNNNNNNXCEXXXXXHPPFFGKHHNNNNNNNNNNNNNNMNNHNNNNNNNNNHKMHHHHHHHFNNNNNNNNNNHHHH","n":["AllocationStatus","Arbitrary","","ArbitraryPointer","Dangling","DeadObject","InBounds","Invariant","","Null","OutOfBounds","PointerGenerator","RoundRobin","any","","","any_alloc_status","any_array","any_in_bounds","any_where","arbitrary","arbitrary_tuple","assert","assume","block_on","block_on_with_spawn","borrow","","","borrow_mut","","","clone","clone_into","clone_to_uninit","concrete_playback_run","contracts","cover","","ensures","eq","float","fmt","","","from","","","futures","generate_arbitrary","generate_float","generate_models","implies","into","","","invariant","is_initialized","kani_intrinsics","kani_lib","kani_mem","kani_mem_init","loop_invariant","mem","modifies","new","pointer_generator","proof","proof_for_contract","ptr","ptr_generator","ptr_generator_fn","recursion","requires","shadow","should_panic","slice","slice_generator","solver","spawn","status","stub","stub_verified","to_owned","try_from","","","try_into","","","type_id","","","unwind","vec","yield_now","ensures","modifies","proof_for_contract","requires","stub_verified","float_to_int_in_range","CanAssumeRunning","CannotAssumeRunning","JoinHandle","RoundRobin","SchedulingAssumption","SchedulingStrategy","block_on","block_on_with_spawn","borrow","","","borrow_mut","","","default","from","","","into","","","into_future","pick_task","","poll","spawn","try_from","","","try_into","","","type_id","","","yield_now","Invariant","is_safe","can_dereference","can_read_unaligned","can_write","can_write_unaligned","checked_align_of_raw","checked_size_of_raw","same_allocation","ShadowMem","borrow","borrow_mut","from","get","into","new","set","try_from","try_into","type_id","any_slice_of_array","any_slice_of_array_mut","any_vec","exact_vec"],"q":[[0,"kani"],[96,"kani::contracts"],[101,"kani::float"],[102,"kani::futures"],[138,"kani::invariant"],[140,"kani::mem"],[147,"kani::shadow"],[158,"kani::slice"],[160,"kani::vec"],[162,"kani::arbitrary_ptr"],[163,"core::ops::function"],[164,"alloc::vec"],[165,"core::fmt"],[166,"core::result"],[167,"core::any"],[168,"core::convert::num"],[169,"core::future::future"],[170,"core::marker"],[171,"core::pin"],[172,"core::task::wake"],[173,"core::task::poll"],[174,"core::option"],[175,"kani_macros"],[176,"kani_core"]],"i":"````d00``00```b1h10```````0l3103333`````3`103103`````103`0```````1```0``````````0``3103103103`````````Cf0``````0CjCb21002102101C`12`321321321``Db````````Df000000000````","f":"`````````````{{}cb}{{}b}{{}d}{{{j{fh}}}{{l{c}}}b}{{}{{n{b}}}}1{ecb{{Ad{{j{c}}}{{A`{Ab}}}}}}``{{Ab{j{Af}}}Ah}{AbAh}``{j{{j{c}}}{}}00{{{j{f}}}{{j{fc}}}{}}00{{{j{d}}}d}{{j{j{fc}}}Ah{}}{{jAj}Ah}{{{Al{{Al{Aj}}}}c}AhAn}`7``{{{j{d}}{j{d}}}Ab}`{{{j{h}}{j{fB`}}}Bb}{{{j{{l{c}}}}{j{fB`}}}BbBd}{{{j{d}}{j{fB`}}}Bb}{cc{}}00`````{{}c{}}00`{lAb}```````{{}h}0``{l}``````````{ld}``{jc{}}{c{{Bf{e}}}{}{}}00{{}{{Bf{c}}}{}}00{jBh}00````````{eAb{}{{Bj{c}}}}``````{ec{}{{Bl{}{{A`{c}}}}}}{{ce}Ah{{Bl{}{{A`{Ah}}}}Bn}C`}{j{{j{c}}}{}}00{{{j{f}}}{{j{fc}}}{}}00{{}Cb}???>>>{{}}{{{j{fC`}}Cd}{{Ch{CdCf}}}}{{{j{fCb}}Cd}{{Ch{CdCf}}}}{{{Cl{{j{fCj}}}}{j{fCn}}}{{D`{c}}}{}}{cCj{{Bl{}{{A`{Ah}}}}Bn}}===<<<;;;{{}{{`{{Bl{}{{A`{Ah}}}}}}}}`{{{j{Db}}}Ab}{{}Ab}000{{}{{Dd{Cd}}}}01`;:{cc{}}{{{j{{Df{c}}}}}cDh}{{}c{}}{c{{Df{c}}}Dh}{{{j{f{Df{c}}}}c}AhDh}{c{{Bf{e}}}{}{}}{{}{{Bf{c}}}{}}{jBh}{{{j{{n{c}}}}}{{j{{Dj{c}}}}}{}}{{{j{f{n{c}}}}}{{j{f{Dj{c}}}}}{}}{{}{{Al{c}}}b}0","D":"Kf","p":[[10,"Arbitrary",0],[6,"AllocationStatus",0,162],[0,"mut"],[5,"PointerGenerator",0,162],[1,"reference",null,null,1],[5,"ArbitraryPointer",0,162],[1,"array"],[17,"Output"],[1,"bool"],[10,"FnOnce",163],[1,"str"],[1,"unit"],[1,"u8"],[5,"Vec",164],[10,"Fn",163],[5,"Formatter",165],[8,"Result",165],[10,"Debug",165],[6,"Result",166,null,1],[5,"TypeId",167],[10,"FloatToInt",168],[10,"Future",169,null,1],[10,"Sync",170],[10,"SchedulingStrategy",102],[5,"RoundRobin",102],[1,"usize"],[6,"SchedulingAssumption",102],[1,"tuple",null,null,1],[5,"JoinHandle",102],[5,"Pin",171],[5,"Context",172],[6,"Poll",173],[10,"Invariant",138],[6,"Option",174,null,1],[5,"ShadowMem",147],[10,"Copy",170],[1,"slice"]],"r":[[0,162],[2,175],[3,162],[4,162],[5,162],[6,162],[7,138],[8,175],[9,162],[10,162],[11,162],[12,102],[15,162],[16,162],[18,162],[21,176],[24,102],[25,102],[26,162],[27,162],[28,162],[29,162],[30,162],[31,162],[32,162],[33,162],[34,162],[39,175],[40,162],[42,162],[43,162],[44,162],[45,162],[46,162],[47,162],[49,176],[50,176],[51,176],[53,162],[54,162],[55,162],[57,162],[58,176],[59,176],[60,176],[61,176],[62,175],[64,175],[65,162],[66,162],[67,175],[68,175],[69,162],[70,176],[71,176],[72,175],[73,175],[75,175],[77,176],[78,175],[79,102],[80,162],[81,175],[82,175],[83,162],[84,162],[85,162],[86,162],[87,162],[88,162],[89,162],[90,162],[91,162],[92,162],[93,175],[95,102],[96,175],[97,175],[98,175],[99,175],[100,175]],"b":[],"c":"OjAAAAAAAAA=","e":"OzAAAAEAAEcAGQAAAAAAAgAAAAgAAAANAAAADwABABIAAAAZAAoAJwAAACkAAAArAAIAMgACAD0AAQBHAAEATQABAFAAAABUAAkAXwABAGcAAQBvAAYAfAAAAH4AAQCBAAgAjAAAAJUAAQCcAAIA","P":[[13,"T"],[14,""],[16,"T"],[17,""],[18,"T"],[19,"T,F"],[22,""],[26,"T"],[32,""],[33,"T"],[34,""],[35,"F"],[37,""],[43,"T"],[44,""],[45,"T"],[53,"U"],[57,""],[83,"T"],[84,"U,T"],[87,"U"],[90,""],[101,"Int,Float"],[108,"T,"],[109,"F,"],[110,"T"],[116,""],[117,"T"],[120,"U"],[123,""],[126,"Future::Output"],[127,"F"],[128,"U,T"],[131,"U"],[134,""],[148,"T"],[152,"U"],[153,"T"],[155,"U,T"],[156,"U"],[157,""],[158,"T"]]}]]')); +if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; +else if (window.initSearch) window.initSearch(searchIndex); +//{"start":39,"fragment_lengths":[5577]} \ No newline at end of file diff --git a/crates/doc/search.desc/kani/kani-desc-0-.js b/crates/doc/search.desc/kani/kani-desc-0-.js new file mode 100644 index 000000000000..461f8df2e081 --- /dev/null +++ b/crates/doc/search.desc/kani/kani-desc-0-.js @@ -0,0 +1 @@ +searchState.loadedDescShard("kani", 0, "Enumeration with the cases currently covered by the …\nAllow users to auto generate Arbitrary implementations by …\nHolds information about a pointer that is generated …\nDangling pointers\nPointer to dead object\nIn bounds pointer (it may be unaligned)\nAllow users to auto generate Invariant implementations by …\nNull pointers\nThe pointer cannot be read / written to for the given type …\nPointer generator that can be used to generate arbitrary …\nThis creates an symbolic valid value of type T. You can …\nCreates a raw pointer with non-deterministic properties.\nCreates a in-bounds raw pointer with non-deterministic …\nThis creates a symbolic valid value of type T. The value …\nThis module introduces the Arbitrary trait as well as …\nThis macro implements kani::Arbitrary on a tuple whose …\nCreates an assertion of the specified condition and …\nCreates an assumption that will be valid after this …\nNOP concrete_playback for type checking during …\nKani implementation of function contracts.\nCreates a cover property with the specified condition and …\nAdd a postcondition to this function.\nThis module contains functions useful for float-related …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nThis module contains functions to work with futures (and …\nimplies!(premise => conclusion) means that if the premise …\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nThis module introduces the Invariant trait as well as its …\nWhether the pointer was generated with an initialized …\nKani intrinsics contains the public APIs used by users to …\nUsers should only need to invoke this.\nAdd a loop invariant to this loop.\nThis module contains functions useful for checking unsafe …\nDeclaration of an explicit write-set for the annotated …\nCreate a new PointerGenerator.\nCreate a pointer generator that fits at least N elements …\nMarks a Kani proof harness\nDesignates this function as a harness to check a function …\nThe pointer that was generated.\nSpecifies that a function contains recursion for contract …\nAdd a precondition to this function.\nThis module contains an API for shadow memory. Shadow …\nSpecifies that a proof harness is expected to panic.**\nSelect the SAT solver to use with CBMC for this harness\nThe expected allocation status.\nSpecify a function/method stub pair to use for proof …\nstub_verified(TARGET) is a harness attribute (to be used on\nSet Loop unwind limit for proof harnesses The attribute …\nAdd a postcondition to this function.\nDeclaration of an explicit write-set for the annotated …\nDesignates this function as a harness to check a function …\nAdd a precondition to this function.\nstub_verified(TARGET) is a harness attribute (to be used on\nReturns whether the given float value satisfies the range …\nResult of spawning a task.\nKeeps cycling through the tasks in a deterministic order\nIndicates to the scheduler whether it can kani::assume …\nTrait that determines the possible sequence of tasks …\nA very simple executor: it polls the future in a busy loop …\nPolls the given future and the tasks it may spawn until …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nPicks the next task to be scheduled whenever the scheduler …\nSpawns a task on the current global executor (which is set …\nSuspends execution of the current future, to allow the …\nThis trait should be used to specify and check type safety …\nChecks that pointer ptr point to a valid value of type T.\nChecks that pointer ptr point to a valid value of type T.\nCheck if the pointer is valid for write access according …\nCheck if the pointer is valid for unaligned write access …\nCompute the size of the val pointed to if safe.\nCompute the size of the val pointed to if it is safe to do …\nCheck if two pointers points to the same allocated object, …\nA shadow memory data structure that contains a …\nReturns the argument unchanged.\nGet the shadow memory value of the given pointer\nCalls U::from(self).\nCreate a new shadow memory instance initialized with the …\nSet the shadow memory value of the given pointer\nGiven an array arr of length LENGTH, this function returns …\nA mutable version of the previous function\nGenerates an arbitrary vector whose length is at most …\nGenerates an arbitrary vector that is exactly EXACT_LENGTH …") \ No newline at end of file diff --git a/crates/doc/settings.html b/crates/doc/settings.html new file mode 100644 index 000000000000..ada0eb811a30 --- /dev/null +++ b/crates/doc/settings.html @@ -0,0 +1 @@ +Settings

Rustdoc settings

Back
\ No newline at end of file diff --git a/crates/doc/src-files.js b/crates/doc/src-files.js new file mode 100644 index 000000000000..59f1ebdf5071 --- /dev/null +++ b/crates/doc/src-files.js @@ -0,0 +1,3 @@ +var srcIndex = new Map(JSON.parse('[["kani",["",[["models",[],["mod.rs"]]],["arbitrary.rs","contracts.rs","futures.rs","invariant.rs","lib.rs","shadow.rs","vec.rs"]]]]')); +createSrcSidebar(); +//{"start":36,"fragment_lengths":[130]} \ No newline at end of file diff --git a/crates/doc/src/kani/arbitrary.rs.html b/crates/doc/src/kani/arbitrary.rs.html new file mode 100644 index 000000000000..88622ecc543c --- /dev/null +++ b/crates/doc/src/kani/arbitrary.rs.html @@ -0,0 +1,51 @@ +arbitrary.rs - source

kani/
arbitrary.rs

+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! This module introduces the `Arbitrary` trait as well as implementation for
+//! primitive types and other std containers.
+
+use crate::Arbitrary;
+
+impl<T> Arbitrary for std::boxed::Box<T>
+where
+    T: Arbitrary,
+{
+    fn any() -> Self {
+        Box::new(T::any())
+    }
+}
+
+impl Arbitrary for std::time::Duration {
+    fn any() -> Self {
+        const NANOS_PER_SEC: u32 = 1_000_000_000;
+        let nanos = u32::any();
+        crate::assume(nanos < NANOS_PER_SEC);
+        std::time::Duration::new(u64::any(), nanos)
+    }
+}
+
\ No newline at end of file diff --git a/crates/doc/src/kani/contracts.rs.html b/crates/doc/src/kani/contracts.rs.html new file mode 100644 index 000000000000..ec598ac4cb22 --- /dev/null +++ b/crates/doc/src/kani/contracts.rs.html @@ -0,0 +1,557 @@ +contracts.rs - source

kani/
contracts.rs

+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! Kani implementation of function contracts.
+//!
+//! Function contracts are still under development. Using the APIs therefore
+//! requires the unstable `-Zfunction-contracts` flag to be passed. You can join
+//! the discussion on contract design by reading our
+//! [RFC](https://model-checking.github.io/kani/rfc/rfcs/0009-function-contracts.html)
+//! and [commenting on the tracking
+//! issue](https://github.com/model-checking/kani/issues/2652).
+//!
+//! The function contract API is expressed as proc-macro attributes, and there
+//! are two parts to it.
+//!
+//! 1. [Contract specification attributes](#specification-attributes-overview):
+//!    [`requires`][macro@requires] and [`ensures`][macro@ensures].
+//! 2. [Contract use attributes](#contract-use-attributes-overview):
+//!    [`proof_for_contract`][macro@proof_for_contract] and
+//!    [`stub_verified`][macro@stub_verified].
+//!
+//! ## Step-by-step Guide
+//!
+//! Let us explore using a workflow involving contracts on the example of a
+//! simple division function `my_div`:
+//!
+//! ```
+//! fn my_div(dividend: usize, divisor: usize) -> usize {
+//!   dividend / divisor
+//! }
+//! ```
+//!
+//! With the contract specification attributes we can specify the behavior of
+//! this function declaratively. The [`requires`][macro@requires] attribute
+//! allows us to declare constraints on what constitutes valid inputs to our
+//! function. In this case we would want to disallow a divisor that is `0`.
+//!
+//! ```
+//! # use kani::requires;
+//! #[requires(divisor != 0)]
+//! # fn my_div(dividend: usize, divisor: usize) -> usize {
+//! #  dividend / divisor
+//! # }
+//! ```
+//!
+//! This is called a precondition, because it is enforced before (pre-) the
+//! function call. As you can see attribute has access to the functions
+//! arguments. The condition itself is just regular Rust code. You can use any
+//! Rust code, including calling functions and methods. However you may not
+//! perform I/O (like [`println!`]) or mutate memory (like [`Vec::push`]).
+//!
+//! The [`ensures`][macro@ensures] attribute on the other hand lets us describe
+//! the output value in terms of the inputs. You may be as (im)precise as you
+//! like in the [`ensures`][macro@ensures] clause, depending on your needs. One
+//! approximation of the result of division for instance could be this:
+//!
+//! ```
+//! # use kani::ensures;
+//! #[ensures(|result : &usize| *result <= dividend)]
+//! # fn my_div(dividend: usize, divisor: usize) -> usize {
+//! #  dividend / divisor
+//! # }
+//! ```
+//!
+//! This is called a postcondition and it also has access to the arguments and
+//! is expressed in regular Rust code. The same restrictions apply as did for
+//! [`requires`][macro@requires]. In addition to the postcondition is expressed
+//! as a closure where the value returned from the function is passed to this
+//! closure by reference.
+//!
+//! You may combine as many [`requires`][macro@requires] and
+//! [`ensures`][macro@ensures] attributes on a single function as you please.
+//! They all get enforced (as if their conditions were `&&`ed together) and the
+//! order does not matter. In our example putting them together looks like this:
+//!
+//! ```
+//! use kani::{requires, ensures};
+//!
+//! #[requires(divisor != 0)]
+//! #[ensures(|result : &usize| *result <= dividend)]
+//! fn my_div(dividend: usize, divisor: usize) -> usize {
+//!   dividend / divisor
+//! }
+//! ```
+//!
+//! Once we are finished specifying our contract we can ask Kani to check it's
+//! validity. For this we need to provide a proof harness that exercises the
+//! function. The harness is created like any other, e.g. as a test-like
+//! function with inputs and using `kani::any` to create arbitrary values.
+//! However we do not need to add any assertions or assumptions about the
+//! inputs, Kani will use the pre- and postconditions we have specified for that
+//! and we use the [`proof_for_contract`][macro@proof_for_contract] attribute
+//! instead of [`proof`](crate::proof) and provide it with the path to the
+//! function we want to check.
+//!
+//! ```
+//! # use kani::{requires, ensures};
+//! #
+//! # #[requires(divisor != 0)]
+//! # #[ensures(|result : &usize| *result <= dividend)]
+//! # fn my_div(dividend: usize, divisor: usize) -> usize {
+//! #   dividend / divisor
+//! # }
+//! #
+//! #[kani::proof_for_contract(my_div)]
+//! fn my_div_harness() {
+//!     my_div(kani::any(), kani::any());
+//! }
+//! ```
+//!
+//! The harness is checked like any other by running `cargo kani` and can be
+//! specifically selected with `--harness my_div_harness`.
+//!
+//! Once we have verified that our contract holds, we can use perhaps it's
+//! coolest feature: verified stubbing. This allows us to use the conditions of
+//! the contract *instead* of it's implementation. This can be very powerful for
+//! expensive implementations (involving loops for instance).
+//!
+//! Verified stubbing is available to any harness via the
+//! [`stub_verified`][macro@stub_verified] harness attribute. We must provide
+//! the attribute with the path to the function to stub, but unlike with
+//! [`stub`](crate::stub) we do not need to provide a function to replace with,
+//! the contract will be used automatically.
+//!
+//! ```
+//! # use kani::{requires, ensures};
+//! #
+//! # #[requires(divisor != 0)]
+//! # #[ensures(|result : &usize| *result <= dividend)]
+//! # fn my_div(dividend: usize, divisor: usize) -> usize {
+//! #   dividend / divisor
+//! # }
+//! #
+//! #[kani::proof]
+//! #[kani::stub_verified(my_div)]
+//! fn use_div() {
+//!   let v = kani::vec::any_vec::<char, 5>();
+//!   let some_idx = my_div(v.len() - 1, 3);
+//!   v[some_idx];
+//! }
+//! ```
+//!
+//! In this example the contract is sufficient to prove that the element access
+//! in the last line cannot be out-of-bounds.
+//!
+//! ## Specification Attributes Overview
+//!
+//! The basic two specification attributes available for describing
+//! function behavior are [`requires`][macro@requires] for preconditions and
+//! [`ensures`][macro@ensures] for postconditions. Both admit arbitrary Rust
+//! expressions as their bodies which may also reference the function arguments
+//! but must not mutate memory or perform I/O. The postcondition may
+//! additionally reference the return value of the function as the variable
+//! `result`.
+//!
+//! In addition Kani provides the [`modifies`](macro@modifies) attribute. This
+//! works a bit different in that it does not contain conditions but a comma
+//! separated sequence of expressions that evaluate to pointers. This attribute
+//! constrains to which memory locations the function is allowed to write. Each
+//! expression can contain arbitrary Rust syntax, though it may not perform side
+//! effects and it is also currently unsound if the expression can panic. For more
+//! information see the [write sets](#write-sets) section.
+//!
+//! During verified stubbing the return value of a function with a contract is
+//! replaced by a call to `kani::any`. As such the return value must implement
+//! the `kani::Arbitrary` trait.
+//!
+//! In Kani, function contracts are optional. As such a function with at least
+//! one specification attribute is considered to "have a contract" and any
+//! absent specification type defaults to its most general interpretation
+//! (`true`). All functions with not a single specification attribute are
+//! considered "not to have a contract" and are ineligible for use as the target
+//! of a [`proof_for_contract`][macro@proof_for_contract] of
+//! [`stub_verified`][macro@stub_verified] attribute.
+//!
+//! ## Contract Use Attributes Overview
+//!
+//! Contract are used both to verify function behavior and to leverage the
+//! verification result as a sound abstraction.
+//!
+//! Verifying function behavior currently requires the designation of at least
+//! one checking harness with the
+//! [`proof_for_contract`](macro@proof_for_contract) attribute. A harness may
+//! only have one `proof_for_contract` attribute and it may not also have a
+//! `proof` attribute.
+//!
+//! The checking harness is expected to set up the arguments that `foo` should
+//! be called with and initialized any `static mut` globals that are reachable.
+//! All of these should be initialized to as general value as possible, usually
+//! achieved using `kani::any`. The harness must call e.g. `foo` at least once
+//! and if `foo` has type parameters, only one instantiation of those parameters
+//! is admissible. Violating either results in a compile error.
+//!
+//! If any inputs have special invariants you *can* use `kani::assume` to
+//! enforce them but this may introduce unsoundness. In general all restrictions
+//! on input parameters should be part of the [`requires`](macro@requires)
+//! clause of the function contract.
+//!
+//! Once the contract has been verified it may be used as a verified stub. For
+//! this the [`stub_verified`](macro@stub_verified) attribute is used.
+//! `stub_verified` is a harness attribute, like
+//! [`unwind`](macro@crate::unwind), meaning it is used on functions that are
+//! annotated with [`proof`](macro@crate::proof). It may also be used on a
+//! `proof_for_contract` proof.
+//!
+//! Unlike `proof_for_contract` multiple `stub_verified` attributes are allowed
+//! on the same proof harness though they must target different functions.
+//!
+//! ## Inductive Verification
+//!
+//! Function contracts by default use inductive verification to efficiently
+//! verify recursive functions. In inductive verification a recursive function
+//! is executed once and every recursive call instead uses the contract
+//! replacement. In this way many recursive calls can be checked with a
+//! single verification pass.
+//!
+//! The downside of inductive verification is that the return value of a
+//! contracted function must implement `kani::Arbitrary`. Due to restrictions to
+//! code generation in proc macros, the contract macros cannot determine reliably
+//! in all cases whether a given function with a contract is recursive. As a
+//! result it conservatively sets up inductive verification for every function
+//! and requires the `kani::Arbitrary` constraint for contract checks.
+//!
+//! If you feel strongly about this issue you can join the discussion on issue
+//! [#2823](https://github.com/model-checking/kani/issues/2823) to enable
+//! opt-out of inductive verification.
+//!
+//! ## Write Sets
+//!
+//! The [`modifies`](macro@modifies) attribute is used to describe which
+//! locations in memory a function may assign to. The attribute contains a comma
+//! separated series of expressions that reference the function arguments.
+//! Syntactically any expression is permissible, though it may not perform side
+//! effects (I/O, mutation) or panic. As an example consider this super simple
+//! function:
+//!
+//! ```
+//! #[kani::modifies(ptr, my_box.as_ref())]
+//! fn a_function(ptr: &mut u32, my_box: &mut Box<u32>) {
+//!     *ptr = 80;
+//!     *my_box.as_mut() = 90;
+//! }
+//! ```
+//!
+//! Because the function performs an observable side-effect (setting both the
+//! value behind the pointer and the value pointed-to by the box) we need to
+//! provide a `modifies` attribute. Otherwise Kani will reject a contract on
+//! this function.
+//!
+//! An expression used in a `modifies` clause must return a pointer to the
+//! location that you would like to allow to be modified. This can be any basic
+//! Rust pointer type (`&T`, `&mut T`, `*const T` or `*mut T`). In addition `T`
+//! must implement [`Arbitrary`](super::Arbitrary). This is used to assign
+//! `kani::any()` to the location when the function is used in a `stub_verified`.
+//!
+//! ## History Expressions
+//!
+//! Additionally, an ensures clause is allowed to refer to the state of the function arguments before function execution and perform simple computations on them
+//! via an `old` monad. Any instance of `old(computation)` will evaluate the
+//! computation before the function is called. It is required that this computation
+//! is effect free and closed with respect to the function arguments.
+//!
+//! For example, the following code passes kani tests:
+//!
+//! ```
+//! #[kani::modifies(a)]
+//! #[kani::ensures(|result| old(*a).wrapping_add(1) == *a)]
+//! #[kani::ensures(|result : &u32| old(*a).wrapping_add(1) == *result)]
+//! fn add1(a : &mut u32) -> u32 {
+//!     *a=a.wrapping_add(1);
+//!     *a
+//! }
+//! ```
+//!
+//! Here, the value stored in `a` is precomputed and remembered after the function
+//! is called, even though the contents of `a` changed during the function execution.
+//!
+pub use super::{ensures, modifies, proof_for_contract, requires, stub_verified};
+
\ No newline at end of file diff --git a/crates/doc/src/kani/futures.rs.html b/crates/doc/src/kani/futures.rs.html new file mode 100644 index 000000000000..5d8dd70664f3 --- /dev/null +++ b/crates/doc/src/kani/futures.rs.html @@ -0,0 +1,475 @@ +futures.rs - source

kani/
futures.rs

+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! This module contains functions to work with futures (and async/.await) in Kani.
+
+use std::{
+    future::Future,
+    pin::Pin,
+    task::{Context, RawWaker, RawWakerVTable, Waker},
+};
+
+/// A very simple executor: it polls the future in a busy loop until completion
+///
+/// This is intended as a drop-in replacement for `futures::block_on`, which Kani cannot handle.
+/// Whereas a clever executor like `block_on` in `futures` or `tokio` would interact with the OS scheduler
+/// to be woken up when a resource becomes available, this is not supported by Kani.
+/// As a consequence, this function completely ignores the waker infrastructure and just polls the given future in a busy loop.
+///
+/// Note that [`spawn`] is not supported with this function. Use [`block_on_with_spawn`] if you need it.
+#[crate::unstable(feature = "async-lib", issue = 2559, reason = "experimental async support")]
+pub fn block_on<T>(mut fut: impl Future<Output = T>) -> T {
+    let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) };
+    let cx = &mut Context::from_waker(&waker);
+    // SAFETY: we shadow the original binding, so it cannot be accessed again for the rest of the scope.
+    // This is the same as what the pin_mut! macro in the futures crate does.
+    let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
+    loop {
+        match fut.as_mut().poll(cx) {
+            std::task::Poll::Ready(res) => return res,
+            std::task::Poll::Pending => continue,
+        }
+    }
+}
+
+/// A dummy waker, which is needed to call [`Future::poll`]
+const NOOP_RAW_WAKER: RawWaker = {
+    #[inline]
+    unsafe fn clone_waker(_: *const ()) -> RawWaker {
+        NOOP_RAW_WAKER
+    }
+
+    #[inline]
+    unsafe fn noop(_: *const ()) {}
+
+    RawWaker::new(std::ptr::null(), &RawWakerVTable::new(clone_waker, noop, noop, noop))
+};
+
+/// The global executor used by [`spawn`] and [`block_on_with_spawn`] to run tasks.
+static mut GLOBAL_EXECUTOR: Option<Scheduler> = None;
+
+type BoxFuture = Pin<Box<dyn Future<Output = ()> + Sync + 'static>>;
+
+/// Indicates to the scheduler whether it can `kani::assume` that the returned task is running.
+///
+/// This is useful if the task was picked nondeterministically using `kani::any()`.
+/// For more information, see [`SchedulingStrategy`].
+pub enum SchedulingAssumption {
+    CanAssumeRunning,
+    CannotAssumeRunning,
+}
+
+/// Trait that determines the possible sequence of tasks scheduling for a harness.
+///
+/// If your harness spawns several tasks, Kani's scheduler has to decide in what order to poll them.
+/// This order may depend on the needs of your verification goal.
+/// For example, you sometimes may wish to verify all possible schedulings, i.e. a nondeterministic scheduling strategy.
+///
+/// Nondeterministic scheduling strategies can be very slow to verify because they require Kani to check a large number of permutations of tasks.
+/// So if you want to verify a harness that uses `spawn`, but don't care about concurrency issues, you can simply use a deterministic scheduling strategy,
+/// such as [`RoundRobin`], which polls each task in turn.
+///
+/// Finally, you have the option of providing your own scheduling strategy by implementing this trait.
+/// This can be useful, for example, if you want to verify that things work correctly for a very specific task ordering.
+pub trait SchedulingStrategy {
+    /// Picks the next task to be scheduled whenever the scheduler needs to pick a task to run next, and whether it can be assumed that the picked task is still running
+    ///
+    /// Tasks are numbered `0..num_tasks`.
+    /// For example, if pick_task(4) returns (2, CanAssumeRunning) than it picked the task with index 2 and allows Kani to `assume` that this task is still running.
+    /// This is useful if the task is chosen nondeterministicall (`kani::any()`) and allows the verifier to discard useless execution branches (such as polling a completed task again).
+    ///
+    /// As a rule of thumb:
+    /// if the scheduling strategy picks the next task nondeterministically (using `kani::any()`), return CanAssumeRunning, otherwise CannotAssumeRunning.
+    /// When returning `CanAssumeRunning`, the scheduler will then assume that the picked task is still running, which cuts off "useless" paths where a completed task is polled again.
+    /// It is even necessary to make things terminate if nondeterminism is involved:
+    /// if we pick the task nondeterministically, and don't have the restriction to still running tasks, we could poll the same task over and over again.
+    ///
+    /// However, for most deterministic scheduling strategies, e.g. the round robin scheduling strategy, assuming that the picked task is still running is generally not possible
+    /// because if that task has ended, we are saying assume(false) and the verification effectively stops (which is undesirable, of course).
+    /// In such cases, return `CannotAssumeRunning` instead.
+    fn pick_task(&mut self, num_tasks: usize) -> (usize, SchedulingAssumption);
+}
+
+/// Keeps cycling through the tasks in a deterministic order
+#[derive(Default)]
+pub struct RoundRobin {
+    index: usize,
+}
+
+impl SchedulingStrategy for RoundRobin {
+    #[inline]
+    fn pick_task(&mut self, num_tasks: usize) -> (usize, SchedulingAssumption) {
+        self.index = (self.index + 1) % num_tasks;
+        (self.index, SchedulingAssumption::CannotAssumeRunning)
+    }
+}
+
+pub(crate) struct Scheduler {
+    tasks: Vec<Option<BoxFuture>>,
+    num_running: usize,
+}
+
+impl Scheduler {
+    /// Creates a scheduler with an empty task list
+    #[inline]
+    pub(crate) const fn new() -> Scheduler {
+        Scheduler { tasks: Vec::new(), num_running: 0 }
+    }
+
+    /// Adds a future to the scheduler's task list, returning a JoinHandle
+    pub(crate) fn spawn<F: Future<Output = ()> + Sync + 'static>(&mut self, fut: F) -> JoinHandle {
+        let index = self.tasks.len();
+        self.tasks.push(Some(Box::pin(fut)));
+        self.num_running += 1;
+        JoinHandle { index }
+    }
+
+    /// Runs the scheduler with the given scheduling plan until all tasks have completed
+    fn run(&mut self, mut scheduling_plan: impl SchedulingStrategy) {
+        let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) };
+        let cx = &mut Context::from_waker(&waker);
+        while self.num_running > 0 {
+            let (index, assumption) = scheduling_plan.pick_task(self.tasks.len());
+            let task = &mut self.tasks[index];
+            if let Some(fut) = task.as_mut() {
+                match fut.as_mut().poll(cx) {
+                    std::task::Poll::Ready(()) => {
+                        self.num_running -= 1;
+                        let _prev = task.take();
+                    }
+                    std::task::Poll::Pending => (),
+                }
+            } else if let SchedulingAssumption::CanAssumeRunning = assumption {
+                crate::assume(false); // useful so that we can assume that a nondeterministically picked task is still running
+            }
+        }
+    }
+
+    /// Polls the given future and the tasks it may spawn until all of them complete
+    fn block_on<F: Future<Output = ()> + Sync + 'static>(
+        &mut self,
+        fut: F,
+        scheduling_plan: impl SchedulingStrategy,
+    ) {
+        self.spawn(fut);
+        self.run(scheduling_plan);
+    }
+}
+
+/// Result of spawning a task.
+///
+/// If you `.await` a JoinHandle, this will wait for the spawned task to complete.
+pub struct JoinHandle {
+    index: usize,
+}
+
+#[allow(static_mut_refs)]
+impl Future for JoinHandle {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll<Self::Output> {
+        if unsafe { GLOBAL_EXECUTOR.as_mut().unwrap().tasks[self.index].is_some() } {
+            std::task::Poll::Pending
+        } else {
+            cx.waker().wake_by_ref(); // For completeness. But Kani currently ignores wakers.
+            std::task::Poll::Ready(())
+        }
+    }
+}
+
+/// Spawns a task on the current global executor (which is set by [`block_on_with_spawn`])
+///
+/// This function can only be called inside a future passed to [`block_on_with_spawn`].
+#[crate::unstable(feature = "async-lib", issue = 2559, reason = "experimental async support")]
+#[allow(static_mut_refs)]
+pub fn spawn<F: Future<Output = ()> + Sync + 'static>(fut: F) -> JoinHandle {
+    unsafe {
+        if let Some(executor) = GLOBAL_EXECUTOR.as_mut() {
+            executor.spawn(fut)
+        } else {
+            // An explicit panic instead of `.expect(...)` has better location information in Kani's output
+            panic!("`spawn` should only be called within `block_on_with_spawn`")
+        }
+    }
+}
+
+/// Polls the given future and the tasks it may spawn until all of them complete
+///
+/// Contrary to [`block_on`], this allows `spawn`ing other futures
+#[crate::unstable(feature = "async-lib", issue = 2559, reason = "experimental async support")]
+#[allow(static_mut_refs)]
+pub fn block_on_with_spawn<F: Future<Output = ()> + Sync + 'static>(
+    fut: F,
+    scheduling_plan: impl SchedulingStrategy,
+) {
+    unsafe {
+        assert!(GLOBAL_EXECUTOR.is_none(), "`block_on_with_spawn` should not be nested");
+        GLOBAL_EXECUTOR = Some(Scheduler::new());
+        GLOBAL_EXECUTOR.as_mut().unwrap().block_on(fut, scheduling_plan);
+        GLOBAL_EXECUTOR = None;
+    }
+}
+
+/// Suspends execution of the current future, to allow the scheduler to poll another future
+///
+/// Specifically, it returns a future that isn't ready until the second time it is polled.
+#[crate::unstable(feature = "async-lib", issue = 2559, reason = "experimental async support")]
+pub fn yield_now() -> impl Future<Output = ()> {
+    struct YieldNow {
+        yielded: bool,
+    }
+
+    impl Future for YieldNow {
+        type Output = ();
+
+        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll<Self::Output> {
+            if self.yielded {
+                cx.waker().wake_by_ref(); // For completeness. But Kani currently ignores wakers.
+                std::task::Poll::Ready(())
+            } else {
+                self.yielded = true;
+                std::task::Poll::Pending
+            }
+        }
+    }
+
+    YieldNow { yielded: false }
+}
+
\ No newline at end of file diff --git a/crates/doc/src/kani/invariant.rs.html b/crates/doc/src/kani/invariant.rs.html new file mode 100644 index 000000000000..e6adaf11856f --- /dev/null +++ b/crates/doc/src/kani/invariant.rs.html @@ -0,0 +1,267 @@ +invariant.rs - source

kani/
invariant.rs

+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! This module introduces the `Invariant` trait as well as its implementation
+//! for primitive types.
+
+/// This trait should be used to specify and check type safety invariants for a
+/// type. For type invariants, we refer to the definitions in the Rust's Unsafe
+/// Code Guidelines Reference:
+/// <https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#validity-and-safety-invariant>
+///
+/// In summary, the reference distinguishes two kinds of type invariants:
+///  - *Validity invariant*: An invariant that all data must uphold any time
+///    it's accessed or copied in a typed manner. This invariant is exploited by
+///    the compiler to perform optimizations.
+///  - *Safety invariant*: An invariant that safe code may assume all data to
+///    uphold. This invariant can be temporarily violated by unsafe code, but
+///    must always be upheld when interfacing with unknown safe code.
+///
+/// Therefore, validity invariants must be upheld at all times, while safety
+/// invariants only need to be upheld at the boundaries to safe code.
+///
+/// Safety invariants are particularly interesting for user-defined types, and
+/// the `Invariant` trait allows you to check them with Kani.
+///
+/// It can also be used in tests. It's a programmatic way to specify (in Rust)
+/// properties over your data types. Since it's written in Rust, it can be used
+/// for static and dynamic checking.
+///
+/// For example, let's say you're creating a type that represents a date:
+///
+/// ```rust
+/// #[derive(kani::Arbitrary)]
+/// pub struct MyDate {
+///   day: u8,
+///   month: u8,
+///   year: i64,
+/// }
+/// ```
+/// You can specify its safety invariant as:
+/// ```rust
+/// # #[derive(kani::Arbitrary)]
+/// # pub struct MyDate {
+/// #  day: u8,
+/// #  month: u8,
+/// #  year: i64,
+/// # }
+/// # fn days_in_month(_: i64, _: u8) -> u8 { 31 }
+///
+/// impl kani::Invariant for MyDate {
+///   fn is_safe(&self) -> bool {
+///     self.month > 0
+///       && self.month <= 12
+///       && self.day > 0
+///       && self.day <= days_in_month(self.year, self.month)
+///   }
+/// }
+/// ```
+/// And use it to check that your APIs are safe:
+/// ```no_run
+/// # use kani::Invariant;
+/// #
+/// # #[derive(kani::Arbitrary)]
+/// # pub struct MyDate {
+/// #  day: u8,
+/// #  month: u8,
+/// #  year: i64,
+/// # }
+/// #
+/// # fn days_in_month(_: i64, _: u8) -> u8 { todo!() }
+/// # fn increase_date(_: &mut MyDate, _: u8) { todo!() }
+/// #
+/// # impl Invariant for MyDate {
+/// #   fn is_safe(&self) -> bool {
+/// #     self.month > 0
+/// #       && self.month <= 12
+/// #       && self.day > 0
+/// #       && self.day <= days_in_month(self.year, self.month)
+/// #   }
+/// # }
+/// #
+/// #[kani::proof]
+/// fn check_increase_date() {
+///   let mut date: MyDate = kani::any();
+///   // Increase date by one day
+///   increase_date(&mut date, 1);
+///   assert!(date.is_safe());
+/// }
+/// ```
+pub trait Invariant
+where
+    Self: Sized,
+{
+    fn is_safe(&self) -> bool;
+}
+
+/// Any value is considered safe for the type
+macro_rules! trivial_invariant {
+    ( $type: ty ) => {
+        impl Invariant for $type {
+            #[inline(always)]
+            fn is_safe(&self) -> bool {
+                true
+            }
+        }
+    };
+}
+
+trivial_invariant!(u8);
+trivial_invariant!(u16);
+trivial_invariant!(u32);
+trivial_invariant!(u64);
+trivial_invariant!(u128);
+trivial_invariant!(usize);
+
+trivial_invariant!(i8);
+trivial_invariant!(i16);
+trivial_invariant!(i32);
+trivial_invariant!(i64);
+trivial_invariant!(i128);
+trivial_invariant!(isize);
+
+// We do not constrain the safety invariant for floating points types.
+// Users can create a new type wrapping the floating point type and define an
+// invariant that checks for NaN, infinite, or subnormal values.
+trivial_invariant!(f32);
+trivial_invariant!(f64);
+trivial_invariant!(f16);
+trivial_invariant!(f128);
+
+trivial_invariant!(());
+trivial_invariant!(bool);
+trivial_invariant!(char);
+
\ No newline at end of file diff --git a/crates/doc/src/kani/lib.rs.html b/crates/doc/src/kani/lib.rs.html new file mode 100644 index 000000000000..a19d3a9af306 --- /dev/null +++ b/crates/doc/src/kani/lib.rs.html @@ -0,0 +1,181 @@ +lib.rs - source

kani/
lib.rs

+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+// Required so we can use kani_macros attributes.
+#![feature(register_tool)]
+#![register_tool(kanitool)]
+// Used for rustc_diagnostic_item.
+// Note: We could use a kanitool attribute instead.
+#![feature(rustc_attrs)]
+// Used to model simd.
+#![feature(repr_simd)]
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+// Features used for tests only.
+#![cfg_attr(test, feature(core_intrinsics, portable_simd))]
+// Required for `rustc_diagnostic_item` and `core_intrinsics`
+#![allow(internal_features)]
+// Required for implementing memory predicates.
+#![feature(layout_for_ptr)]
+#![feature(ptr_metadata)]
+#![feature(f16)]
+#![feature(f128)]
+#![feature(convert_float_to_int)]
+
+// Allow us to use `kani::` to access crate features.
+extern crate self as kani;
+
+pub mod arbitrary;
+#[cfg(feature = "concrete_playback")]
+mod concrete_playback;
+pub mod futures;
+pub mod invariant;
+pub mod shadow;
+pub mod vec;
+
+mod models;
+
+#[cfg(feature = "concrete_playback")]
+pub use concrete_playback::concrete_playback_run;
+pub use invariant::Invariant;
+
+#[cfg(not(feature = "concrete_playback"))]
+/// NOP `concrete_playback` for type checking during verification mode.
+pub fn concrete_playback_run<F: Fn()>(_: Vec<Vec<u8>>, _: F) {
+    unreachable!("Concrete playback does not work during verification")
+}
+
+pub use futures::{RoundRobin, block_on, block_on_with_spawn, spawn, yield_now};
+
+// Kani proc macros must be in a separate crate
+pub use kani_macros::*;
+
+// Declare common Kani API such as assume, assert
+kani_core::kani_lib!(kani);
+
+// Used to bind `core::assert` to a different name to avoid possible name conflicts if a
+// crate uses `extern crate std as core`. See
+// https://github.com/model-checking/kani/issues/1949 and https://github.com/model-checking/kani/issues/2187
+#[doc(hidden)]
+#[cfg(not(feature = "concrete_playback"))]
+pub use core::assert as __kani__workaround_core_assert;
+
+#[macro_export]
+macro_rules! cover {
+    () => {
+        kani::cover(true, "cover location");
+    };
+    ($cond:expr $(,)?) => {
+        kani::cover($cond, concat!("cover condition: ", stringify!($cond)));
+    };
+    ($cond:expr, $msg:literal) => {
+        kani::cover($cond, $msg);
+    };
+}
+
+/// `implies!(premise => conclusion)` means that if the `premise` is true, so
+/// must be the `conclusion`.
+///
+/// This simply expands to `!premise || conclusion` and is intended to make checks more readable,
+/// as the concept of an implication is more natural to think about than its expansion.
+#[macro_export]
+macro_rules! implies {
+    ($premise:expr => $conclusion:expr) => {
+        !($premise) || ($conclusion)
+    };
+}
+
+pub(crate) use kani_macros::unstable_feature as unstable;
+
+pub mod contracts;
+
\ No newline at end of file diff --git a/crates/doc/src/kani/models/mod.rs.html b/crates/doc/src/kani/models/mod.rs.html new file mode 100644 index 000000000000..cbaa022aed01 --- /dev/null +++ b/crates/doc/src/kani/models/mod.rs.html @@ -0,0 +1,453 @@ +mod.rs - source

kani/models/
mod.rs

+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+//! Contains definitions that Kani compiler may use to model functions that are not suitable for
+//! verification or functions without a body, such as intrinsics.
+//!
+//! Note that these are models that Kani uses by default; thus, we keep them separate from stubs.
+
+// Definitions in this module are not meant to be visible to the end user, only the compiler.
+#[allow(dead_code)]
+mod intrinsics {
+    use std::fmt::Debug;
+    use std::mem::size_of;
+
+    /// Similar definition to portable SIMD.
+    /// We cannot reuse theirs since TRUE and FALSE defs are private.
+    /// We leave this private today, since this is not necessarily a final solution, so we don't
+    /// want users relying on this.
+    /// Our definitions are also a bit more permissive to comply with the platform intrinsics.
+    pub(super) trait MaskElement: PartialEq + Debug {
+        const TRUE: Self;
+        const FALSE: Self;
+    }
+
+    macro_rules! impl_element {
+        { $ty:ty } => {
+            impl MaskElement for $ty {
+                const TRUE: Self = -1;
+                const FALSE: Self = 0;
+            }
+        }
+    }
+
+    macro_rules! impl_unsigned_element {
+        { $ty:ty } => {
+            impl MaskElement for $ty {
+                // Note that in the declaration of the intrinsic it is documented that the lane
+                // values should be -1 or 0:
+                // <https://github.com/rust-lang/rust/blob/338cfd3/library/portable-simd/crates/core_simd/src/intrinsics.rs#L134-L144>
+                //
+                // However, MIRI and the Rust compiler seems to accept unsigned values and they
+                // use their binary representation. Thus, that's what we use for now.
+                /// All bits are 1 which represents TRUE.
+                const TRUE: Self = <$ty>::MAX;
+                /// All bits are 0 which represents FALSE.
+                const FALSE: Self = 0;
+            }
+        }
+    }
+
+    impl_element! { i8 }
+    impl_element! { i16 }
+    impl_element! { i32 }
+    impl_element! { i64 }
+    impl_element! { i128 }
+    impl_element! { isize }
+
+    impl_unsigned_element! { u8 }
+    impl_unsigned_element! { u16 }
+    impl_unsigned_element! { u32 }
+    impl_unsigned_element! { u64 }
+    impl_unsigned_element! { u128 }
+    impl_unsigned_element! { usize }
+
+    /// Calculate the minimum number of lanes to represent a mask
+    /// Logic similar to `bitmask_len` from `portable_simd`.
+    /// <https://github.com/rust-lang/portable-simd/blob/490b5cf/crates/core_simd/src/masks/to_bitmask.rs#L75-L79>
+    pub(super) const fn mask_len(len: usize) -> usize {
+        len.div_ceil(8)
+    }
+
+    #[cfg(target_endian = "little")]
+    unsafe fn simd_bitmask_impl<T, const LANES: usize>(input: &[T; LANES]) -> [u8; mask_len(LANES)]
+    where
+        T: MaskElement,
+    {
+        let mut mask_array = [0; mask_len(LANES)];
+        for lane in (0..input.len()).rev() {
+            let byte = lane / 8;
+            let mask = &mut mask_array[byte];
+            let shift_mask = *mask << 1;
+            *mask = if input[lane] == T::TRUE {
+                shift_mask | 0x1
+            } else {
+                assert_eq!(input[lane], T::FALSE, "Masks values should either be 0 or -1");
+                shift_mask
+            };
+        }
+        mask_array
+    }
+
+    /// Stub for simd_bitmask.
+    ///
+    /// It will reduce a simd vector (TxN), into an integer of size S (in bits), where S >= N.
+    /// Each bit of the output will represent a lane from the input. A lane value of all 0's will be
+    /// translated to 1b0, while all 1's will be translated to 1b1.
+    ///
+    /// In order to be able to do this pragmatically, we take additional parameters that are filled
+    /// by the compiler.
+    #[rustc_diagnostic_item = "KaniModelSimdBitmask"]
+    pub(super) unsafe fn simd_bitmask<T, U, E, const LANES: usize>(input: T) -> U
+    where
+        [u8; mask_len(LANES)]: Sized,
+        E: MaskElement,
+    {
+        // These checks are compiler sanity checks to ensure we are not doing anything invalid.
+        assert_eq!(
+            size_of::<U>(),
+            size_of::<[u8; mask_len(LANES)]>(),
+            "Expected size of return type and mask lanes to match",
+        );
+        assert_eq!(
+            size_of::<T>(),
+            size_of::<Simd::<E, LANES>>(),
+            "Expected size of input and lanes to match",
+        );
+
+        let data = &*(&input as *const T as *const [E; LANES]);
+        let mask = simd_bitmask_impl(data);
+        (&mask as *const [u8; mask_len(LANES)] as *const U).read()
+    }
+
+    /// Structure used for sanity check our parameters.
+    #[repr(simd)]
+    struct Simd<T, const LANES: usize>([T; LANES]);
+}
+
+#[cfg(test)]
+mod test {
+    use super::intrinsics as kani_intrinsic;
+    use std::intrinsics::simd::*;
+    use std::{fmt::Debug, simd::*};
+
+    /// Test that the `simd_bitmask` model is equivalent to the intrinsic for all true and all false
+    /// masks with lanes represented using i16.
+    #[test]
+    fn test_bitmask_i16() {
+        check_portable_bitmask::<_, i16, 16, u16>(mask16x16::splat(false));
+        check_portable_bitmask::<_, i16, 16, u16>(mask16x16::splat(true));
+    }
+
+    /// Tests that the model correctly fails if an invalid value is given.
+    #[test]
+    #[should_panic(expected = "Masks values should either be 0 or -1")]
+    fn test_invalid_bitmask() {
+        let invalid_mask = unsafe { mask32x16::from_int_unchecked(i32x16::splat(10)) };
+        assert_eq!(
+            unsafe { kani_intrinsic::simd_bitmask::<_, u16, i32, 16>(invalid_mask) },
+            u16::MAX
+        );
+    }
+
+    /// Tests that the model correctly fails if the size parameter of the mask doesn't match the
+    /// expected number of bytes in the representation.
+    #[test]
+    #[should_panic(expected = "Expected size of return type and mask lanes to match")]
+    fn test_invalid_generics() {
+        let mask = mask32x16::splat(false);
+        assert_eq!(unsafe { kani_intrinsic::simd_bitmask::<_, u16, i32, 2>(mask) }, u16::MAX);
+    }
+
+    /// Test that the `simd_bitmask` model is equivalent to the intrinsic for a few random values.
+    /// These values shouldn't be symmetric and ensure that we also handle endianness correctly.
+    #[test]
+    fn test_bitmask_i32() {
+        check_portable_bitmask::<_, i32, 8, u8>(mask32x8::from([
+            true, true, false, true, false, false, false, true,
+        ]));
+
+        check_portable_bitmask::<_, i32, 4, u8>(mask32x4::from([true, false, false, true]));
+    }
+
+    #[repr(simd)]
+    #[derive(Clone, Debug)]
+    struct CustomMask<T, const LANES: usize>([T; LANES]);
+
+    /// Check that the bitmask model can handle odd size SIMD arrays.
+    /// Since the portable_simd restricts the number of lanes, we have to use our own custom SIMD.
+    #[test]
+    fn test_bitmask_odd_lanes() {
+        check_bitmask::<_, [u8; 3], i128, 23>(CustomMask([0i128; 23]));
+        check_bitmask::<_, [u8; 9], i128, 70>(CustomMask([-1i128; 70]));
+    }
+
+    /// Compare the value returned by our model and the portable simd representation.
+    fn check_portable_bitmask<T, E, const LANES: usize, M>(mask: Mask<T, LANES>)
+    where
+        T: std::simd::MaskElement,
+        LaneCount<LANES>: SupportedLaneCount,
+        E: kani_intrinsic::MaskElement,
+        [u8; kani_intrinsic::mask_len(LANES)]: Sized,
+        u64: From<M>,
+    {
+        assert_eq!(
+            unsafe { u64::from(kani_intrinsic::simd_bitmask::<_, M, E, LANES>(mask)) },
+            mask.to_bitmask()
+        );
+    }
+
+    /// Compare the value returned by our model and the simd_bitmask intrinsic.
+    fn check_bitmask<T, U, E, const LANES: usize>(mask: T)
+    where
+        T: Clone,
+        U: PartialEq + Debug,
+        E: kani_intrinsic::MaskElement,
+        [u8; kani_intrinsic::mask_len(LANES)]: Sized,
+    {
+        assert_eq!(
+            unsafe { kani_intrinsic::simd_bitmask::<_, U, E, LANES>(mask.clone()) },
+            unsafe { simd_bitmask::<T, U>(mask) }
+        );
+    }
+
+    /// Similar to portable simd_harness.
+    #[test]
+    fn check_mask_harness() {
+        // From array doesn't work either. Manually build [false, true, false, true]
+        let mut mask = mask32x4::splat(false);
+        mask.set(1, true);
+        mask.set(3, true);
+        let bitmask = mask.to_bitmask();
+        assert_eq!(bitmask, 0b1010);
+
+        let kani_mask = unsafe { u64::from(kani_intrinsic::simd_bitmask::<_, u8, u32, 4>(mask)) };
+        assert_eq!(kani_mask, bitmask);
+    }
+}
+
\ No newline at end of file diff --git a/crates/doc/src/kani/shadow.rs.html b/crates/doc/src/kani/shadow.rs.html new file mode 100644 index 000000000000..5b343dad2dc6 --- /dev/null +++ b/crates/doc/src/kani/shadow.rs.html @@ -0,0 +1,167 @@ +shadow.rs - source

kani/
shadow.rs

+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! This module contains an API for shadow memory.
+//! Shadow memory is a mechanism by which we can store metadata on memory
+//! locations, e.g. whether a memory location is initialized.
+//!
+//! The main data structure provided by this module is the `ShadowMem` struct,
+//! which allows us to store metadata on a given memory location.
+//!
+//! # Example
+//!
+//! ```no_run
+//! use kani::shadow::ShadowMem;
+//! use std::alloc::{alloc, Layout};
+//!
+//! let mut sm = ShadowMem::new(false);
+//!
+//! unsafe {
+//!     let ptr = alloc(Layout::new::<u8>());
+//!     // assert the memory location is not initialized
+//!     assert!(!sm.get(ptr));
+//!     // write to the memory location
+//!     *ptr = 42;
+//!     // update the shadow memory to indicate that this location is now initialized
+//!     sm.set(ptr, true);
+//! }
+//! ```
+
+const MAX_NUM_OBJECTS: usize = 1024;
+const MAX_OBJECT_SIZE: usize = 64;
+
+const MAX_NUM_OBJECTS_ASSERT_MSG: &str = "The number of objects exceeds the maximum number supported by Kani's shadow memory model (1024)";
+const MAX_OBJECT_SIZE_ASSERT_MSG: &str =
+    "The object size exceeds the maximum size supported by Kani's shadow memory model (64)";
+
+/// A shadow memory data structure that contains a two-dimensional array of a
+/// generic type `T`.
+/// Each element of the outer array represents an object, and each element of
+/// the inner array represents a byte in the object.
+pub struct ShadowMem<T: Copy> {
+    mem: [[T; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS],
+}
+
+impl<T: Copy> ShadowMem<T> {
+    /// Create a new shadow memory instance initialized with the given value
+    #[crate::unstable(
+        feature = "ghost-state",
+        issue = 3184,
+        reason = "experimental ghost state/shadow memory API"
+    )]
+    pub const fn new(val: T) -> Self {
+        Self { mem: [[val; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] }
+    }
+
+    /// Get the shadow memory value of the given pointer
+    #[crate::unstable(
+        feature = "ghost-state",
+        issue = 3184,
+        reason = "experimental ghost state/shadow memory API"
+    )]
+    pub fn get<U>(&self, ptr: *const U) -> T {
+        let obj = crate::mem::pointer_object(ptr);
+        let offset = crate::mem::pointer_offset(ptr);
+        crate::assert(obj < MAX_NUM_OBJECTS, MAX_NUM_OBJECTS_ASSERT_MSG);
+        crate::assert(offset < MAX_OBJECT_SIZE, MAX_OBJECT_SIZE_ASSERT_MSG);
+        self.mem[obj][offset]
+    }
+
+    /// Set the shadow memory value of the given pointer
+    #[crate::unstable(
+        feature = "ghost-state",
+        issue = 3184,
+        reason = "experimental ghost state/shadow memory API"
+    )]
+    pub fn set<U>(&mut self, ptr: *const U, val: T) {
+        let obj = crate::mem::pointer_object(ptr);
+        let offset = crate::mem::pointer_offset(ptr);
+        crate::assert(obj < MAX_NUM_OBJECTS, MAX_NUM_OBJECTS_ASSERT_MSG);
+        crate::assert(offset < MAX_OBJECT_SIZE, MAX_OBJECT_SIZE_ASSERT_MSG);
+        self.mem[obj][offset] = val;
+    }
+}
+
\ No newline at end of file diff --git a/crates/doc/src/kani/vec.rs.html b/crates/doc/src/kani/vec.rs.html new file mode 100644 index 000000000000..f481486ccc3f --- /dev/null +++ b/crates/doc/src/kani/vec.rs.html @@ -0,0 +1,63 @@ +vec.rs - source

kani/
vec.rs

+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+use crate::{Arbitrary, any, any_where};
+
+/// Generates an arbitrary vector whose length is at most MAX_LENGTH.
+pub fn any_vec<T, const MAX_LENGTH: usize>() -> Vec<T>
+where
+    T: Arbitrary,
+{
+    let real_length: usize = any_where(|sz| *sz <= MAX_LENGTH);
+    match real_length {
+        0 => vec![],
+        exact if exact == MAX_LENGTH => exact_vec::<T, MAX_LENGTH>(),
+        _ => {
+            let mut any_vec = exact_vec::<T, MAX_LENGTH>();
+            any_vec.truncate(real_length);
+            any_vec.shrink_to_fit();
+            assert!(any_vec.capacity() == any_vec.len());
+            any_vec
+        }
+    }
+}
+
+/// Generates an arbitrary vector that is exactly EXACT_LENGTH long.
+pub fn exact_vec<T, const EXACT_LENGTH: usize>() -> Vec<T>
+where
+    T: Arbitrary,
+{
+    let boxed_array: Box<[T; EXACT_LENGTH]> = Box::new(any());
+    <[T]>::into_vec(boxed_array)
+}
+
\ No newline at end of file diff --git a/crates/doc/static.files/COPYRIGHT-565f0803.txt b/crates/doc/static.files/COPYRIGHT-565f0803.txt new file mode 100644 index 000000000000..111340298c5e --- /dev/null +++ b/crates/doc/static.files/COPYRIGHT-565f0803.txt @@ -0,0 +1,50 @@ +# REUSE-IgnoreStart + +These documentation pages include resources by third parties. This copyright +file applies only to those resources. The following third party resources are +included, and carry their own copyright notices and license terms: + +* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): + + Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ + with Reserved Font Name Fira Sans. + + Copyright (c) 2014, Telefonica S.A. + + Licensed under the SIL Open Font License, Version 1.1. + See FiraSans-LICENSE.txt. + +* rustdoc.css, main.js, and playpen.js: + + Copyright 2015 The Rust Developers. + Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or + the MIT license (LICENSE-MIT.txt) at your option. + +* normalize.css: + + Copyright (c) Nicolas Gallagher and Jonathan Neal. + Licensed under the MIT license (see LICENSE-MIT.txt). + +* Source Code Pro (SourceCodePro-Regular.ttf.woff2, + SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): + + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark + of Adobe Systems Incorporated in the United States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceCodePro-LICENSE.txt. + +* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, + SourceSerif4-It.ttf.woff2, SourceSerif4-Semibold.ttf.woff2): + + Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name + 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United + States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceSerif4-LICENSE.md. + +This copyright file is intended to be distributed with rustdoc output. + +# REUSE-IgnoreEnd diff --git a/crates/doc/static.files/FiraMono-Medium-86f75c8c.woff2 b/crates/doc/static.files/FiraMono-Medium-86f75c8c.woff2 new file mode 100644 index 000000000000..610e9b2071ec Binary files /dev/null and b/crates/doc/static.files/FiraMono-Medium-86f75c8c.woff2 differ diff --git a/crates/doc/static.files/FiraMono-Regular-87c26294.woff2 b/crates/doc/static.files/FiraMono-Regular-87c26294.woff2 new file mode 100644 index 000000000000..9fa44b7cc2d3 Binary files /dev/null and b/crates/doc/static.files/FiraMono-Regular-87c26294.woff2 differ diff --git a/crates/doc/static.files/FiraSans-Italic-81dc35de.woff2 b/crates/doc/static.files/FiraSans-Italic-81dc35de.woff2 new file mode 100644 index 000000000000..3f63664fee6d Binary files /dev/null and b/crates/doc/static.files/FiraSans-Italic-81dc35de.woff2 differ diff --git a/crates/doc/static.files/FiraSans-LICENSE-05ab6dbd.txt b/crates/doc/static.files/FiraSans-LICENSE-05ab6dbd.txt new file mode 100644 index 000000000000..d7e9c149b7ea --- /dev/null +++ b/crates/doc/static.files/FiraSans-LICENSE-05ab6dbd.txt @@ -0,0 +1,98 @@ +// REUSE-IgnoreStart + +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/crates/doc/static.files/FiraSans-Medium-e1aa3f0a.woff2 b/crates/doc/static.files/FiraSans-Medium-e1aa3f0a.woff2 new file mode 100644 index 000000000000..7a1e5fc548ef Binary files /dev/null and b/crates/doc/static.files/FiraSans-Medium-e1aa3f0a.woff2 differ diff --git a/crates/doc/static.files/FiraSans-MediumItalic-ccf7e434.woff2 b/crates/doc/static.files/FiraSans-MediumItalic-ccf7e434.woff2 new file mode 100644 index 000000000000..2d08f9f7d45f Binary files /dev/null and b/crates/doc/static.files/FiraSans-MediumItalic-ccf7e434.woff2 differ diff --git a/crates/doc/static.files/FiraSans-Regular-0fe48ade.woff2 b/crates/doc/static.files/FiraSans-Regular-0fe48ade.woff2 new file mode 100644 index 000000000000..e766e06ccb0d Binary files /dev/null and b/crates/doc/static.files/FiraSans-Regular-0fe48ade.woff2 differ diff --git a/crates/doc/static.files/LICENSE-APACHE-a60eea81.txt b/crates/doc/static.files/LICENSE-APACHE-a60eea81.txt new file mode 100644 index 000000000000..16fe87b06e80 --- /dev/null +++ b/crates/doc/static.files/LICENSE-APACHE-a60eea81.txt @@ -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/crates/doc/static.files/LICENSE-MIT-23f18e03.txt b/crates/doc/static.files/LICENSE-MIT-23f18e03.txt new file mode 100644 index 000000000000..31aa79387f27 --- /dev/null +++ b/crates/doc/static.files/LICENSE-MIT-23f18e03.txt @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/crates/doc/static.files/NanumBarunGothic-13b3dcba.ttf.woff2 b/crates/doc/static.files/NanumBarunGothic-13b3dcba.ttf.woff2 new file mode 100644 index 000000000000..1866ad4bcea6 Binary files /dev/null and b/crates/doc/static.files/NanumBarunGothic-13b3dcba.ttf.woff2 differ diff --git a/crates/doc/static.files/NanumBarunGothic-LICENSE-a37d393b.txt b/crates/doc/static.files/NanumBarunGothic-LICENSE-a37d393b.txt new file mode 100644 index 000000000000..4b3edc29eb90 --- /dev/null +++ b/crates/doc/static.files/NanumBarunGothic-LICENSE-a37d393b.txt @@ -0,0 +1,103 @@ +// REUSE-IgnoreStart + +Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), + +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, +NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, +Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, +NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, +Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/crates/doc/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 b/crates/doc/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 new file mode 100644 index 000000000000..462c34efcd9d Binary files /dev/null and b/crates/doc/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceCodePro-LICENSE-67f54ca7.txt b/crates/doc/static.files/SourceCodePro-LICENSE-67f54ca7.txt new file mode 100644 index 000000000000..0d2941e148df --- /dev/null +++ b/crates/doc/static.files/SourceCodePro-LICENSE-67f54ca7.txt @@ -0,0 +1,97 @@ +// REUSE-IgnoreStart + +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/crates/doc/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 b/crates/doc/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 new file mode 100644 index 000000000000..10b558e0b69a Binary files /dev/null and b/crates/doc/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2 b/crates/doc/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2 new file mode 100644 index 000000000000..5ec64eef0ec9 Binary files /dev/null and b/crates/doc/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 b/crates/doc/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 new file mode 100644 index 000000000000..181a07f63bef Binary files /dev/null and b/crates/doc/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 b/crates/doc/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 new file mode 100644 index 000000000000..2ae08a7bedfe Binary files /dev/null and b/crates/doc/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceSerif4-LICENSE-a2cfd9d5.md b/crates/doc/static.files/SourceSerif4-LICENSE-a2cfd9d5.md new file mode 100644 index 000000000000..175fa4f47aec --- /dev/null +++ b/crates/doc/static.files/SourceSerif4-LICENSE-a2cfd9d5.md @@ -0,0 +1,98 @@ + + +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. +Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + + diff --git a/crates/doc/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 b/crates/doc/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 new file mode 100644 index 000000000000..0263fc304226 Binary files /dev/null and b/crates/doc/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 b/crates/doc/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 new file mode 100644 index 000000000000..dd55f4e95ec9 Binary files /dev/null and b/crates/doc/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 differ diff --git a/crates/doc/static.files/favicon-044be391.svg b/crates/doc/static.files/favicon-044be391.svg new file mode 100644 index 000000000000..8b34b511989e --- /dev/null +++ b/crates/doc/static.files/favicon-044be391.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/crates/doc/static.files/favicon-32x32-6580c154.png b/crates/doc/static.files/favicon-32x32-6580c154.png new file mode 100644 index 000000000000..69b8613ce150 Binary files /dev/null and b/crates/doc/static.files/favicon-32x32-6580c154.png differ diff --git a/crates/doc/static.files/main-4d63596a.js b/crates/doc/static.files/main-4d63596a.js new file mode 100644 index 000000000000..f8a8c3da8557 --- /dev/null +++ b/crates/doc/static.files/main-4d63596a.js @@ -0,0 +1,11 @@ +"use strict";window.RUSTDOC_TOOLTIP_HOVER_MS=300;window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS=450;function resourcePath(basename,extension){return getVar("root-path")+basename+getVar("resource-suffix")+extension}function hideMain(){addClass(document.getElementById(MAIN_ID),"hidden");const toggle=document.getElementById("toggle-all-docs");if(toggle){toggle.setAttribute("disabled","disabled")}}function showMain(){const main=document.getElementById(MAIN_ID);if(!main){return}removeClass(main,"hidden");const mainHeading=main.querySelector(".main-heading");if(mainHeading&&window.searchState.rustdocToolbar){if(window.searchState.rustdocToolbar.parentElement){window.searchState.rustdocToolbar.parentElement.removeChild(window.searchState.rustdocToolbar,)}mainHeading.appendChild(window.searchState.rustdocToolbar)}const toggle=document.getElementById("toggle-all-docs");if(toggle){toggle.removeAttribute("disabled")}}window.rootPath=getVar("root-path");window.currentCrate=getVar("current-crate");function setMobileTopbar(){const mobileTopbar=document.querySelector(".mobile-topbar");const locationTitle=document.querySelector(".sidebar h2.location");if(mobileTopbar){const mobileTitle=document.createElement("h2");mobileTitle.className="location";if(hasClass(document.querySelector(".rustdoc"),"crate")){mobileTitle.innerHTML=`Crate ${window.currentCrate}`}else if(locationTitle){mobileTitle.innerHTML=locationTitle.innerHTML}mobileTopbar.appendChild(mobileTitle)}}function getVirtualKey(ev){if("key"in ev&&typeof ev.key!=="undefined"){return ev.key}const c=ev.charCode||ev.keyCode;if(c===27){return"Escape"}return String.fromCharCode(c)}const MAIN_ID="main-content";const SETTINGS_BUTTON_ID="settings-menu";const ALTERNATIVE_DISPLAY_ID="alternative-display";const NOT_DISPLAYED_ID="not-displayed";const HELP_BUTTON_ID="help-button";function getSettingsButton(){return document.getElementById(SETTINGS_BUTTON_ID)}function getHelpButton(){return document.getElementById(HELP_BUTTON_ID)}function getNakedUrl(){return window.location.href.split("?")[0].split("#")[0]}function insertAfter(newNode,referenceNode){referenceNode.parentNode.insertBefore(newNode,referenceNode.nextSibling)}function getOrCreateSection(id,classes){let el=document.getElementById(id);if(!el){el=document.createElement("section");el.id=id;el.className=classes;insertAfter(el,document.getElementById(MAIN_ID))}return el}function getAlternativeDisplayElem(){return getOrCreateSection(ALTERNATIVE_DISPLAY_ID,"content hidden")}function getNotDisplayedElem(){return getOrCreateSection(NOT_DISPLAYED_ID,"hidden")}function switchDisplayedElement(elemToDisplay){const el=getAlternativeDisplayElem();if(el.children.length>0){getNotDisplayedElem().appendChild(el.firstElementChild)}if(elemToDisplay===null){addClass(el,"hidden");showMain();return}el.appendChild(elemToDisplay);hideMain();removeClass(el,"hidden");const mainHeading=elemToDisplay.querySelector(".main-heading");if(mainHeading&&window.searchState.rustdocToolbar){if(window.searchState.rustdocToolbar.parentElement){window.searchState.rustdocToolbar.parentElement.removeChild(window.searchState.rustdocToolbar,)}mainHeading.appendChild(window.searchState.rustdocToolbar)}}function browserSupportsHistoryApi(){return window.history&&typeof window.history.pushState==="function"}function preLoadCss(cssUrl){const link=document.createElement("link");link.href=cssUrl;link.rel="preload";link.as="style";document.getElementsByTagName("head")[0].appendChild(link)}(function(){const isHelpPage=window.location.pathname.endsWith("/help.html");function loadScript(url,errorCallback){const script=document.createElement("script");script.src=url;if(errorCallback!==undefined){script.onerror=errorCallback}document.head.append(script)}const settingsButton=getSettingsButton();if(settingsButton){settingsButton.onclick=event=>{if(event.ctrlKey||event.altKey||event.metaKey){return}window.hideAllModals(false);addClass(getSettingsButton(),"rotate");event.preventDefault();loadScript(getVar("static-root-path")+getVar("settings-js"));setTimeout(()=>{const themes=getVar("themes").split(",");for(const theme of themes){if(theme!==""){preLoadCss(getVar("root-path")+theme+".css")}}},0)}}window.searchState={rustdocToolbar:document.querySelector("rustdoc-toolbar"),loadingText:"Loading search results...",input:document.getElementsByClassName("search-input")[0],outputElement:()=>{let el=document.getElementById("search");if(!el){el=document.createElement("section");el.id="search";getNotDisplayedElem().appendChild(el)}return el},title:document.title,titleBeforeSearch:document.title,timeout:null,currentTab:0,focusedByTab:[null,null,null],clearInputTimeout:()=>{if(window.searchState.timeout!==null){clearTimeout(window.searchState.timeout);window.searchState.timeout=null}},isDisplayed:()=>{const outputElement=window.searchState.outputElement();return outputElement&&outputElement.parentElement&&outputElement.parentElement.id===ALTERNATIVE_DISPLAY_ID},focus:()=>{window.searchState.input&&window.searchState.input.focus()},defocus:()=>{window.searchState.input&&window.searchState.input.blur()},showResults:search=>{if(search===null||typeof search==="undefined"){search=window.searchState.outputElement()}switchDisplayedElement(search);window.searchState.mouseMovedAfterSearch=false;document.title=window.searchState.title},removeQueryParameters:()=>{document.title=window.searchState.titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.hash)}},hideResults:()=>{switchDisplayedElement(null);window.searchState.removeQueryParameters()},getQueryStringParams:()=>{const params={};window.location.search.substring(1).split("&").map(s=>{const pair=s.split("=").map(x=>x.replace(/\+/g," "));params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1])});return params},setup:()=>{const search_input=window.searchState.input;if(!search_input){return}let searchLoaded=false;function sendSearchForm(){document.getElementsByClassName("search-form")[0].submit()}function loadSearch(){if(!searchLoaded){searchLoaded=true;loadScript(getVar("static-root-path")+getVar("search-js"),sendSearchForm);loadScript(resourcePath("search-index",".js"),sendSearchForm)}}search_input.addEventListener("focus",()=>{window.searchState.origPlaceholder=search_input.placeholder;search_input.placeholder="Type your search here.";loadSearch()});if(search_input.value!==""){loadSearch()}const params=window.searchState.getQueryStringParams();if(params.search!==undefined){window.searchState.setLoadingSearch();loadSearch()}},setLoadingSearch:()=>{const search=window.searchState.outputElement();if(!search){return}search.innerHTML="

"+window.searchState.loadingText+"

";window.searchState.showResults(search)},descShards:new Map(),loadDesc:async function({descShard,descIndex}){if(descShard.promise===null){descShard.promise=new Promise((resolve,reject)=>{descShard.resolve=resolve;const ds=descShard;const fname=`${ds.crate}-desc-${ds.shard}-`;const url=resourcePath(`search.desc/${descShard.crate}/${fname}`,".js",);loadScript(url,reject)})}const list=await descShard.promise;return list[descIndex]},loadedDescShard:function(crate,shard,data){this.descShards.get(crate)[shard].resolve(data.split("\n"))},};const toggleAllDocsId="toggle-all-docs";let savedHash="";function handleHashes(ev){if(ev!==null&&window.searchState.isDisplayed()&&ev.newURL){switchDisplayedElement(null);const hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.search+"#"+hash)}const elem=document.getElementById(hash);if(elem){elem.scrollIntoView()}}const pageId=window.location.hash.replace(/^#/,"");if(savedHash!==pageId){savedHash=pageId;if(pageId!==""){expandSection(pageId)}}if(savedHash.startsWith("impl-")){const splitAt=savedHash.indexOf("/");if(splitAt!==-1){const implId=savedHash.slice(0,splitAt);const assocId=savedHash.slice(splitAt+1);const implElems=document.querySelectorAll(`details > summary > section[id^="${implId}"]`,);onEachLazy(implElems,implElem=>{const numbered=/^(.+?)-([0-9]+)$/.exec(implElem.id);if(implElem.id!==implId&&(!numbered||numbered[1]!==implId)){return false}return onEachLazy(implElem.parentElement.parentElement.querySelectorAll(`[id^="${assocId}"]`),item=>{const numbered=/^(.+?)-([0-9]+)$/.exec(item.id);if(item.id===assocId||(numbered&&numbered[1]===assocId)){openParentDetails(item);item.scrollIntoView();setTimeout(()=>{window.location.replace("#"+item.id)},0);return true}},)})}}}function onHashChange(ev){hideSidebar();handleHashes(ev)}function openParentDetails(elem){while(elem){if(elem.tagName==="DETAILS"){elem.open=true}elem=elem.parentNode}}function expandSection(id){openParentDetails(document.getElementById(id))}function handleEscape(ev){searchState.clearInputTimeout();searchState.hideResults();ev.preventDefault();searchState.defocus();window.hideAllModals(true)}function handleShortcut(ev){const disableShortcuts=getSettingValue("disable-shortcuts")==="true";if(ev.ctrlKey||ev.altKey||ev.metaKey||disableShortcuts){return}if(document.activeElement.tagName==="INPUT"&&document.activeElement.type!=="checkbox"&&document.activeElement.type!=="radio"){switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break}}else{switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;case"s":case"S":case"/":ev.preventDefault();searchState.focus();break;case"+":ev.preventDefault();expandAllDocs();break;case"-":ev.preventDefault();collapseAllDocs();break;case"?":showHelp();break;default:break}}}document.addEventListener("keypress",handleShortcut);document.addEventListener("keydown",handleShortcut);function addSidebarItems(){if(!window.SIDEBAR_ITEMS){return}const sidebar=document.getElementById("rustdoc-modnav");function block(shortty,id,longty){const filtered=window.SIDEBAR_ITEMS[shortty];if(!filtered){return}const modpath=hasClass(document.querySelector(".rustdoc"),"mod")?"../":"";const h3=document.createElement("h3");h3.innerHTML=`${longty}`;const ul=document.createElement("ul");ul.className="block "+shortty;for(const name of filtered){let path;if(shortty==="mod"){path=`${modpath}${name}/index.html`}else{path=`${modpath}${shortty}.${name}.html`}let current_page=document.location.href.toString();if(current_page.endsWith("/")){current_page+="index.html"}const link=document.createElement("a");link.href=path;link.textContent=name;const li=document.createElement("li");if(link.href===current_page){li.classList.add("current")}li.appendChild(link);ul.appendChild(li)}sidebar.appendChild(h3);sidebar.appendChild(ul)}if(sidebar){block("primitive","primitives","Primitive Types");block("mod","modules","Modules");block("macro","macros","Macros");block("struct","structs","Structs");block("enum","enums","Enums");block("constant","constants","Constants");block("static","static","Statics");block("trait","traits","Traits");block("fn","functions","Functions");block("type","types","Type Aliases");block("union","unions","Unions");block("foreigntype","foreign-types","Foreign Types");block("keyword","keywords","Keywords");block("attr","attributes","Attribute Macros");block("derive","derives","Derive Macros");block("traitalias","trait-aliases","Trait Aliases")}}window.register_implementors=imp=>{const implementors=document.getElementById("implementors-list");const synthetic_implementors=document.getElementById("synthetic-implementors-list");const inlined_types=new Set();const TEXT_IDX=0;const SYNTHETIC_IDX=1;const TYPES_IDX=2;if(synthetic_implementors){onEachLazy(synthetic_implementors.getElementsByClassName("impl"),el=>{const aliases=el.getAttribute("data-aliases");if(!aliases){return}aliases.split(",").forEach(alias=>{inlined_types.add(alias)})})}let currentNbImpls=implementors.getElementsByClassName("impl").length;const traitName=document.querySelector(".main-heading h1 > .trait").textContent;const baseIdName="impl-"+traitName+"-";const libs=Object.getOwnPropertyNames(imp);const script=document.querySelector("script[data-ignore-extern-crates]");const ignoreExternCrates=new Set((script?script.getAttribute("data-ignore-extern-crates"):"").split(","),);for(const lib of libs){if(lib===window.currentCrate||ignoreExternCrates.has(lib)){continue}const structs=imp[lib];struct_loop:for(const struct of structs){const list=struct[SYNTHETIC_IDX]?synthetic_implementors:implementors;if(struct[SYNTHETIC_IDX]){for(const struct_type of struct[TYPES_IDX]){if(inlined_types.has(struct_type)){continue struct_loop}inlined_types.add(struct_type)}}const code=document.createElement("h3");code.innerHTML=struct[TEXT_IDX];addClass(code,"code-header");onEachLazy(code.getElementsByTagName("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});const currentId=baseIdName+currentNbImpls;const anchor=document.createElement("a");anchor.href="#"+currentId;addClass(anchor,"anchor");const display=document.createElement("div");display.id=currentId;addClass(display,"impl");display.appendChild(anchor);display.appendChild(code);list.appendChild(display);currentNbImpls+=1}}};if(window.pending_implementors){window.register_implementors(window.pending_implementors)}window.register_type_impls=imp=>{if(!imp||!imp[window.currentCrate]){return}window.pending_type_impls=null;const idMap=new Map();let implementations=document.getElementById("implementations-list");let trait_implementations=document.getElementById("trait-implementations-list");let trait_implementations_header=document.getElementById("trait-implementations");const script=document.querySelector("script[data-self-path]");const selfPath=script?script.getAttribute("data-self-path"):null;const mainContent=document.querySelector("#main-content");const sidebarSection=document.querySelector(".sidebar section");let methods=document.querySelector(".sidebar .block.method");let associatedTypes=document.querySelector(".sidebar .block.associatedtype");let associatedConstants=document.querySelector(".sidebar .block.associatedconstant");let sidebarTraitList=document.querySelector(".sidebar .block.trait-implementation");for(const impList of imp[window.currentCrate]){const types=impList.slice(2);const text=impList[0];const isTrait=impList[1]!==0;const traitName=impList[1];if(types.indexOf(selfPath)===-1){continue}let outputList=isTrait?trait_implementations:implementations;if(outputList===null){const outputListName=isTrait?"Trait Implementations":"Implementations";const outputListId=isTrait?"trait-implementations-list":"implementations-list";const outputListHeaderId=isTrait?"trait-implementations":"implementations";const outputListHeader=document.createElement("h2");outputListHeader.id=outputListHeaderId;outputListHeader.innerText=outputListName;outputList=document.createElement("div");outputList.id=outputListId;if(isTrait){const link=document.createElement("a");link.href=`#${outputListHeaderId}`;link.innerText="Trait Implementations";const h=document.createElement("h3");h.appendChild(link);trait_implementations=outputList;trait_implementations_header=outputListHeader;sidebarSection.appendChild(h);sidebarTraitList=document.createElement("ul");sidebarTraitList.className="block trait-implementation";sidebarSection.appendChild(sidebarTraitList);mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}else{implementations=outputList;if(trait_implementations){mainContent.insertBefore(outputListHeader,trait_implementations_header);mainContent.insertBefore(outputList,trait_implementations_header)}else{const mainContent=document.querySelector("#main-content");mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}}}const template=document.createElement("template");template.innerHTML=text;onEachLazy(template.content.querySelectorAll("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});onEachLazy(template.content.querySelectorAll("[id]"),el=>{let i=0;if(idMap.has(el.id)){i=idMap.get(el.id)}else if(document.getElementById(el.id)){i=1;while(document.getElementById(`${el.id}-${2 * i}`)){i=2*i}while(document.getElementById(`${el.id}-${i}`)){i+=1}}if(i!==0){const oldHref=`#${el.id}`;const newHref=`#${el.id}-${i}`;el.id=`${el.id}-${i}`;onEachLazy(template.content.querySelectorAll("a[href]"),link=>{if(link.getAttribute("href")===oldHref){link.href=newHref}})}idMap.set(el.id,i+1)});const templateAssocItems=template.content.querySelectorAll("section.tymethod, "+"section.method, section.associatedtype, section.associatedconstant");if(isTrait){const li=document.createElement("li");const a=document.createElement("a");a.href=`#${template.content.querySelector(".impl").id}`;a.textContent=traitName;li.appendChild(a);sidebarTraitList.append(li)}else{onEachLazy(templateAssocItems,item=>{let block=hasClass(item,"associatedtype")?associatedTypes:(hasClass(item,"associatedconstant")?associatedConstants:(methods));if(!block){const blockTitle=hasClass(item,"associatedtype")?"Associated Types":(hasClass(item,"associatedconstant")?"Associated Constants":("Methods"));const blockClass=hasClass(item,"associatedtype")?"associatedtype":(hasClass(item,"associatedconstant")?"associatedconstant":("method"));const blockHeader=document.createElement("h3");const blockLink=document.createElement("a");blockLink.href="#implementations";blockLink.innerText=blockTitle;blockHeader.appendChild(blockLink);block=document.createElement("ul");block.className=`block ${blockClass}`;const insertionReference=methods||sidebarTraitList;if(insertionReference){const insertionReferenceH=insertionReference.previousElementSibling;sidebarSection.insertBefore(blockHeader,insertionReferenceH);sidebarSection.insertBefore(block,insertionReferenceH)}else{sidebarSection.appendChild(blockHeader);sidebarSection.appendChild(block)}if(hasClass(item,"associatedtype")){associatedTypes=block}else if(hasClass(item,"associatedconstant")){associatedConstants=block}else{methods=block}}const li=document.createElement("li");const a=document.createElement("a");a.innerText=item.id.split("-")[0].split(".")[1];a.href=`#${item.id}`;li.appendChild(a);block.appendChild(li)})}outputList.appendChild(template.content)}for(const list of[methods,associatedTypes,associatedConstants,sidebarTraitList]){if(!list){continue}const newChildren=Array.prototype.slice.call(list.children);newChildren.sort((a,b)=>{const aI=a.innerText;const bI=b.innerText;return aIbI?1:0});list.replaceChildren(...newChildren)}};if(window.pending_type_impls){window.register_type_impls(window.pending_type_impls)}function addSidebarCrates(){if(!window.ALL_CRATES){return}const sidebarElems=document.getElementById("rustdoc-modnav");if(!sidebarElems){return}const h3=document.createElement("h3");h3.innerHTML="Crates";const ul=document.createElement("ul");ul.className="block crate";for(const crate of window.ALL_CRATES){const link=document.createElement("a");link.href=window.rootPath+crate+"/index.html";link.textContent=crate;const li=document.createElement("li");if(window.rootPath!=="./"&&crate===window.currentCrate){li.className="current"}li.appendChild(link);ul.appendChild(li)}sidebarElems.appendChild(h3);sidebarElems.appendChild(ul)}function expandAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);removeClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hasClass(e,"type-contents-toggle")&&!hasClass(e,"more-examples-toggle")){e.open=true}});innerToggle.children[0].innerText="Summary"}function collapseAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);addClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(e.parentNode.id!=="implementations-list"||(!hasClass(e,"implementors-toggle")&&!hasClass(e,"type-contents-toggle"))){e.open=false}});innerToggle.children[0].innerText="Show all"}function toggleAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);if(!innerToggle){return}if(hasClass(innerToggle,"will-expand")){expandAllDocs()}else{collapseAllDocs()}}(function(){const toggles=document.getElementById(toggleAllDocsId);if(toggles){toggles.onclick=toggleAllDocs}const hideMethodDocs=getSettingValue("auto-hide-method-docs")==="true";const hideImplementations=getSettingValue("auto-hide-trait-implementations")==="true";const hideLargeItemContents=getSettingValue("auto-hide-large-items")!=="false";function setImplementorsTogglesOpen(id,open){const list=document.getElementById(id);if(list!==null){onEachLazy(list.getElementsByClassName("implementors-toggle"),e=>{e.open=open})}}if(hideImplementations){setImplementorsTogglesOpen("trait-implementations-list",false);setImplementorsTogglesOpen("blanket-implementations-list",false)}onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hideLargeItemContents&&hasClass(e,"type-contents-toggle")){e.open=true}if(hideMethodDocs&&hasClass(e,"method-toggle")){e.open=false}})}());window.rustdoc_add_line_numbers_to_examples=()=>{if(document.querySelector(".rustdoc.src")){return}onEachLazy(document.querySelectorAll(":not(.scraped-example) > .example-wrap > pre:not(.example-line-numbers)",),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");if(line_numbers.length>0){return}const count=x.textContent.split("\n").length;const elems=[];for(let i=0;i{onEachLazy(document.querySelectorAll(".example-wrap > .example-line-numbers"),x=>{x.parentNode.removeChild(x)})};if(getSettingValue("line-numbers")==="true"){window.rustdoc_add_line_numbers_to_examples()}function showSidebar(){window.hideAllModals(false);const sidebar=document.getElementsByClassName("sidebar")[0];addClass(sidebar,"shown")}function hideSidebar(){const sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"shown")}window.addEventListener("resize",()=>{if(window.CURRENT_TOOLTIP_ELEMENT){const base=window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;const force_visible=base.TOOLTIP_FORCE_VISIBLE;hideTooltip(false);if(force_visible){showTooltip(base);base.TOOLTIP_FORCE_VISIBLE=true}}});const mainElem=document.getElementById(MAIN_ID);if(mainElem){mainElem.addEventListener("click",hideSidebar)}onEachLazy(document.querySelectorAll("a[href^='#']"),el=>{el.addEventListener("click",()=>{expandSection(el.hash.slice(1));hideSidebar()})});onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"),el=>{el.addEventListener("click",e=>{if(e.target.tagName!=="SUMMARY"&&e.target.tagName!=="A"){e.preventDefault()}})});function showTooltip(e){const notable_ty=e.getAttribute("data-notable-ty");if(!window.NOTABLE_TRAITS&¬able_ty){const data=document.getElementById("notable-traits-data");if(data){window.NOTABLE_TRAITS=JSON.parse(data.innerText)}else{throw new Error("showTooltip() called with notable without any notable traits!")}}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE===e){clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);return}window.hideAllModals(false);const wrapper=document.createElement("div");if(notable_ty){wrapper.innerHTML="
"+window.NOTABLE_TRAITS[notable_ty]+"
"}else{const ttl=e.getAttribute("title");if(ttl!==null){e.setAttribute("data-title",ttl);e.removeAttribute("title")}const dttl=e.getAttribute("data-title");if(dttl!==null){const titleContent=document.createElement("div");titleContent.className="content";titleContent.appendChild(document.createTextNode(dttl));wrapper.appendChild(titleContent)}}wrapper.className="tooltip popover";const focusCatcher=document.createElement("div");focusCatcher.setAttribute("tabindex","0");focusCatcher.onfocus=hideTooltip;wrapper.appendChild(focusCatcher);const pos=e.getBoundingClientRect();wrapper.style.top=(pos.top+window.scrollY+pos.height)+"px";wrapper.style.left=0;wrapper.style.right="auto";wrapper.style.visibility="hidden";document.body.appendChild(wrapper);const wrapperPos=wrapper.getBoundingClientRect();const finalPos=pos.left+window.scrollX-wrapperPos.width+24;if(finalPos>0){wrapper.style.left=finalPos+"px"}else{wrapper.style.setProperty("--popover-arrow-offset",(wrapperPos.right-pos.right+4)+"px",)}wrapper.style.visibility="";window.CURRENT_TOOLTIP_ELEMENT=wrapper;window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE=e;clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);wrapper.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}clearTooltipHoverTimeout(e)};wrapper.onpointerleave=ev=>{if(ev.pointerType!=="mouse"||!(ev.relatedTarget instanceof HTMLElement)){return}if(!e.TOOLTIP_FORCE_VISIBLE&&!e.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(wrapper,"fade-out")}}}function setTooltipHoverTimeout(element,show){clearTooltipHoverTimeout(element);if(!show&&!window.CURRENT_TOOLTIP_ELEMENT){return}if(show&&window.CURRENT_TOOLTIP_ELEMENT){return}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE!==element){return}element.TOOLTIP_HOVER_TIMEOUT=setTimeout(()=>{if(show){showTooltip(element)}else if(!element.TOOLTIP_FORCE_VISIBLE){hideTooltip(false)}},show?window.RUSTDOC_TOOLTIP_HOVER_MS:window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS)}function clearTooltipHoverTimeout(element){if(element.TOOLTIP_HOVER_TIMEOUT!==undefined){removeClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out");clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);delete element.TOOLTIP_HOVER_TIMEOUT}}function tooltipBlurHandler(event){if(window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget)){setTimeout(()=>hideTooltip(false),0)}}function hideTooltip(focus){if(window.CURRENT_TOOLTIP_ELEMENT){if(window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE){if(focus){window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus()}window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE=false}document.body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);window.CURRENT_TOOLTIP_ELEMENT=null}}onEachLazy(document.getElementsByClassName("tooltip"),e=>{e.onclick=()=>{e.TOOLTIP_FORCE_VISIBLE=e.TOOLTIP_FORCE_VISIBLE?false:true;if(window.CURRENT_TOOLTIP_ELEMENT&&!e.TOOLTIP_FORCE_VISIBLE){hideTooltip(true)}else{showTooltip(e);window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex","0");window.CURRENT_TOOLTIP_ELEMENT.focus();window.CURRENT_TOOLTIP_ELEMENT.onblur=tooltipBlurHandler}return false};e.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointermove=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out")}}});const sidebar_menu_toggle=document.getElementsByClassName("sidebar-menu-toggle")[0];if(sidebar_menu_toggle){sidebar_menu_toggle.addEventListener("click",()=>{const sidebar=document.getElementsByClassName("sidebar")[0];if(!hasClass(sidebar,"shown")){showSidebar()}else{hideSidebar()}})}function helpBlurHandler(event){if(!getHelpButton().contains(document.activeElement)&&!getHelpButton().contains(event.relatedTarget)&&!getSettingsButton().contains(document.activeElement)&&!getSettingsButton().contains(event.relatedTarget)){window.hidePopoverMenus()}}function buildHelpMenu(){const book_info=document.createElement("span");const drloChannel=`https://doc.rust-lang.org/${getVar("channel")}`;book_info.className="top";book_info.innerHTML=`You can find more information in \ +the rustdoc book.`;const shortcuts=[["?","Show this help dialog"],["S / /","Focus the search field"],["↑","Move up in search results"],["↓","Move down in search results"],["← / →","Switch result tab (when results focused)"],["⏎","Go to active search result"],["+","Expand all sections"],["-","Collapse all sections"],].map(x=>"
"+x[0].split(" ").map((y,index)=>((index&1)===0?""+y+"":" "+y+" ")).join("")+"
"+x[1]+"
").join("");const div_shortcuts=document.createElement("div");addClass(div_shortcuts,"shortcuts");div_shortcuts.innerHTML="

Keyboard Shortcuts

"+shortcuts+"
";const infos=[`For a full list of all search features, take a look \ + here.`,"Prefix searches with a type followed by a colon (e.g., fn:) to \ + restrict the search to a given item kind.","Accepted kinds are: fn, mod, struct, \ + enum, trait, type, macro, \ + and const.","Search functions by type signature (e.g., vec -> usize or \ + -> vec or String, enum:Cow -> bool)","You can look for items with an exact name by putting double quotes around \ + your request: \"string\"",`Look for functions that accept or return \ + slices and \ + arrays by writing square \ + brackets (e.g., -> [u8] or [] -> Option)`,"Look for items inside another one by searching for a path: vec::Vec",].map(x=>"

"+x+"

").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="

Search Tricks

"+infos;const rustdoc_version=document.createElement("span");rustdoc_version.className="bottom";const rustdoc_version_code=document.createElement("code");rustdoc_version_code.innerText="rustdoc "+getVar("rustdoc-version");rustdoc_version.appendChild(rustdoc_version_code);const container=document.createElement("div");if(!isHelpPage){container.className="popover"}container.id="help";container.style.display="none";const side_by_side=document.createElement("div");side_by_side.className="side-by-side";side_by_side.appendChild(div_shortcuts);side_by_side.appendChild(div_infos);container.appendChild(book_info);container.appendChild(side_by_side);container.appendChild(rustdoc_version);if(isHelpPage){const help_section=document.createElement("section");help_section.appendChild(container);document.getElementById("main-content").appendChild(help_section);container.style.display="block"}else{const help_button=getHelpButton();help_button.appendChild(container);container.onblur=helpBlurHandler;help_button.onblur=helpBlurHandler;help_button.children[0].onblur=helpBlurHandler}return container}window.hideAllModals=switchFocus=>{hideSidebar();window.hidePopoverMenus();hideTooltip(switchFocus)};window.hidePopoverMenus=()=>{onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"),elem=>{elem.style.display="none"});const button=getHelpButton();if(button){removeClass(button,"help-open")}};function getHelpMenu(buildNeeded){let menu=getHelpButton().querySelector(".popover");if(!menu&&buildNeeded){menu=buildHelpMenu()}return menu}function showHelp(){const button=getHelpButton();addClass(button,"help-open");button.querySelector("a").focus();const menu=getHelpMenu(true);if(menu.style.display==="none"){window.hideAllModals();menu.style.display=""}}const helpLink=document.querySelector(`#${HELP_BUTTON_ID} > a`);if(isHelpPage){buildHelpMenu()}else if(helpLink){helpLink.addEventListener("click",event=>{if(!helpLink.contains(helpLink)||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault();const menu=getHelpMenu(true);const shouldShowHelp=menu.style.display==="none";if(shouldShowHelp){showHelp()}else{window.hidePopoverMenus()}})}setMobileTopbar();addSidebarItems();addSidebarCrates();onHashChange(null);window.addEventListener("hashchange",onHashChange);searchState.setup()}());(function(){const SIDEBAR_MIN=100;const SIDEBAR_MAX=500;const RUSTDOC_MOBILE_BREAKPOINT=700;const BODY_MIN=400;const SIDEBAR_VANISH_THRESHOLD=SIDEBAR_MIN/2;const sidebarButton=document.getElementById("sidebar-button");if(sidebarButton){sidebarButton.addEventListener("click",e=>{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false");if(document.querySelector(".rustdoc.src")){window.rustdocToggleSrcSidebar()}e.preventDefault()})}let currentPointerId=null;let desiredSidebarSize=null;let pendingSidebarResizingFrame=false;const resizer=document.querySelector(".sidebar-resizer");const sidebar=document.querySelector(".sidebar");if(!resizer||!sidebar){return}const isSrcPage=hasClass(document.body,"src");const hideSidebar=function(){if(isSrcPage){window.rustdocCloseSourceSidebar();updateLocalStorage("src-sidebar-width",null);document.documentElement.style.removeProperty("--src-sidebar-width");sidebar.style.removeProperty("--src-sidebar-width");resizer.style.removeProperty("--src-sidebar-width")}else{addClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","true");updateLocalStorage("desktop-sidebar-width",null);document.documentElement.style.removeProperty("--desktop-sidebar-width");sidebar.style.removeProperty("--desktop-sidebar-width");resizer.style.removeProperty("--desktop-sidebar-width")}};const showSidebar=function(){if(isSrcPage){window.rustdocShowSourceSidebar()}else{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false")}};const changeSidebarSize=function(size){if(isSrcPage){updateLocalStorage("src-sidebar-width",size.toString());sidebar.style.setProperty("--src-sidebar-width",size+"px");resizer.style.setProperty("--src-sidebar-width",size+"px")}else{updateLocalStorage("desktop-sidebar-width",size.toString());sidebar.style.setProperty("--desktop-sidebar-width",size+"px");resizer.style.setProperty("--desktop-sidebar-width",size+"px")}};const isSidebarHidden=function(){return isSrcPage?!hasClass(document.documentElement,"src-sidebar-expanded"):hasClass(document.documentElement,"hide-sidebar")};const resize=function(e){if(currentPointerId===null||currentPointerId!==e.pointerId){return}e.preventDefault();const pos=e.clientX-3;if(pos=SIDEBAR_MIN){if(isSidebarHidden()){showSidebar()}const constrainedPos=Math.min(pos,window.innerWidth-BODY_MIN,SIDEBAR_MAX);changeSidebarSize(constrainedPos);desiredSidebarSize=constrainedPos;if(pendingSidebarResizingFrame!==false){clearTimeout(pendingSidebarResizingFrame)}pendingSidebarResizingFrame=setTimeout(()=>{if(currentPointerId===null||pendingSidebarResizingFrame===false){return}pendingSidebarResizingFrame=false;document.documentElement.style.setProperty("--resizing-sidebar-width",desiredSidebarSize+"px",)},100)}};window.addEventListener("resize",()=>{if(window.innerWidth=(window.innerWidth-BODY_MIN)){changeSidebarSize(window.innerWidth-BODY_MIN)}else if(desiredSidebarSize!==null&&desiredSidebarSize>SIDEBAR_MIN){changeSidebarSize(desiredSidebarSize)}});const stopResize=function(e){if(currentPointerId===null){return}if(e){e.preventDefault()}desiredSidebarSize=sidebar.getBoundingClientRect().width;removeClass(resizer,"active");window.removeEventListener("pointermove",resize,false);window.removeEventListener("pointerup",stopResize,false);removeClass(document.documentElement,"sidebar-resizing");document.documentElement.style.removeProperty("--resizing-sidebar-width");if(resizer.releasePointerCapture){resizer.releasePointerCapture(currentPointerId);currentPointerId=null}};const initResize=function(e){if(currentPointerId!==null||e.altKey||e.ctrlKey||e.metaKey||e.button!==0){return}if(resizer.setPointerCapture){resizer.setPointerCapture(e.pointerId);if(!resizer.hasPointerCapture(e.pointerId)){resizer.releasePointerCapture(e.pointerId);return}currentPointerId=e.pointerId}window.hideAllModals(false);e.preventDefault();window.addEventListener("pointermove",resize,false);window.addEventListener("pointercancel",stopResize,false);window.addEventListener("pointerup",stopResize,false);addClass(resizer,"active");addClass(document.documentElement,"sidebar-resizing");const pos=e.clientX-sidebar.offsetLeft-3;document.documentElement.style.setProperty("--resizing-sidebar-width",pos+"px");desiredSidebarSize=null};resizer.addEventListener("pointerdown",initResize,false)}());(function(){function copyContentToClipboard(content){if(content===null){return}const el=document.createElement("textarea");el.value=content;el.setAttribute("readonly","");el.style.position="absolute";el.style.left="-9999px";document.body.appendChild(el);el.select();document.execCommand("copy");document.body.removeChild(el)}function copyButtonAnimation(button){button.classList.add("clicked");if(button.reset_button_timeout!==undefined){clearTimeout(button.reset_button_timeout)}button.reset_button_timeout=setTimeout(()=>{button.reset_button_timeout=undefined;button.classList.remove("clicked")},1000)}const but=document.getElementById("copy-path");if(!but){return}but.onclick=()=>{const[item,module]=document.title.split(" in ");const path=[item];if(module!==undefined){path.unshift(module)}copyContentToClipboard(path.join("::"));copyButtonAnimation(but)};function copyCode(codeElem){if(!codeElem){return}copyContentToClipboard(codeElem.textContent)}function getExampleWrap(event){const target=event.target;if(target instanceof HTMLElement){let elem=target;while(elem!==null&&!hasClass(elem,"example-wrap")){if(elem===document.body||elem.tagName==="A"||elem.tagName==="BUTTON"||hasClass(elem,"docblock")){return null}elem=elem.parentElement}return elem}else{return null}}function addCopyButton(event){const elem=getExampleWrap(event);if(elem===null){return}elem.removeEventListener("mouseover",addCopyButton);const parent=document.createElement("div");parent.className="button-holder";const runButton=elem.querySelector(".test-arrow");if(runButton!==null){parent.appendChild(runButton)}elem.appendChild(parent);const copyButton=document.createElement("button");copyButton.className="copy-button";copyButton.title="Copy code to clipboard";copyButton.addEventListener("click",()=>{copyCode(elem.querySelector("pre > code"));copyButtonAnimation(copyButton)});parent.appendChild(copyButton);if(!elem.parentElement||!elem.parentElement.classList.contains("scraped-example")||!window.updateScrapedExample){return}const scrapedWrapped=elem.parentElement;window.updateScrapedExample(scrapedWrapped,parent)}function showHideCodeExampleButtons(event){const elem=getExampleWrap(event);if(elem===null){return}let buttons=elem.querySelector(".button-holder");if(buttons===null){addCopyButton(event);buttons=elem.querySelector(".button-holder");if(buttons===null){return}}buttons.classList.toggle("keep-visible")}onEachLazy(document.querySelectorAll(".docblock .example-wrap"),elem=>{elem.addEventListener("mouseover",addCopyButton);elem.addEventListener("click",showHideCodeExampleButtons)})}()) \ No newline at end of file diff --git a/crates/doc/static.files/normalize-9960930a.css b/crates/doc/static.files/normalize-9960930a.css new file mode 100644 index 000000000000..469959f13729 --- /dev/null +++ b/crates/doc/static.files/normalize-9960930a.css @@ -0,0 +1,2 @@ + /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} \ No newline at end of file diff --git a/crates/doc/static.files/noscript-893ab5e7.css b/crates/doc/static.files/noscript-893ab5e7.css new file mode 100644 index 000000000000..a6c18ecaf63f --- /dev/null +++ b/crates/doc/static.files/noscript-893ab5e7.css @@ -0,0 +1 @@ + #main-content .attributes{margin-left:0 !important;}#copy-path,#sidebar-button,.sidebar-resizer{display:none !important;}nav.sub{display:none;}.src .sidebar{display:none;}.notable-traits{display:none;}:root,:root:not([data-theme]){--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--code-example-button-color:#7f7f7f;--code-example-button-hover-color:#595959;--settings-menu-filter:invert(50%);--settings-menu-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}@media (prefers-color-scheme:dark){:root,:root:not([data-theme]){--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--code-example-button-color:#7f7f7f;--code-example-button-hover-color:#a5a5a5;--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--settings-menu-filter:invert(50%);--settings-menu-hover-filter:invert(65%);--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}} \ No newline at end of file diff --git a/crates/doc/static.files/rust-logo-9a9549ea.svg b/crates/doc/static.files/rust-logo-9a9549ea.svg new file mode 100644 index 000000000000..62424d8ffd76 --- /dev/null +++ b/crates/doc/static.files/rust-logo-9a9549ea.svg @@ -0,0 +1,61 @@ + + + diff --git a/crates/doc/static.files/rustdoc-cf3c48c1.css b/crates/doc/static.files/rustdoc-cf3c48c1.css new file mode 100644 index 000000000000..160fd3b3b195 --- /dev/null +++ b/crates/doc/static.files/rustdoc-cf3c48c1.css @@ -0,0 +1,53 @@ + :root{--nav-sub-mobile-padding:8px;--search-typename-width:6.75rem;--desktop-sidebar-width:200px;--src-sidebar-width:300px;--desktop-sidebar-z-index:100;--sidebar-elems-left-padding:24px;--clipboard-image:url('data:image/svg+xml,\ +\ +\ +');--copy-path-height:34px;--copy-path-width:33px;--checkmark-image:url('data:image/svg+xml,\ +\ +');--button-left-margin:4px;--button-border-radius:2px;--toolbar-button-border-radius:6px;--code-block-border-radius:6px;--impl-items-indent:0.3em;--docblock-indent:24px;--font-family:"Source Serif 4",NanumBarunGothic,serif;--font-family-code:"Source Code Pro",monospace;}:root.sans-serif{--font-family:"Fira Sans",sans-serif;--font-family-code:"Fira Mono",monospace;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular-0fe48ade.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:italic;font-weight:400;src:local('Fira Sans Italic'),url("FiraSans-Italic-81dc35de.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:500;src:local('Fira Sans Medium'),url("FiraSans-Medium-e1aa3f0a.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:italic;font-weight:500;src:local('Fira Sans Medium Italic'),url("FiraSans-MediumItalic-ccf7e434.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Mono';font-style:normal;font-weight:400;src:local('Fira Mono'),url("FiraMono-Regular-87c26294.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Mono';font-style:normal;font-weight:500;src:local('Fira Mono Medium'),url("FiraMono-Medium-86f75c8c.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:400;src:local('Source Serif 4'),url("SourceSerif4-Regular-6b053e98.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:italic;font-weight:400;src:local('Source Serif 4 Italic'),url("SourceSerif4-It-ca3b17ed.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:500;src:local('Source Serif 4 Semibold'),url("SourceSerif4-Semibold-457a13ac.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:700;src:local('Source Serif 4 Bold'),url("SourceSerif4-Bold-6d4fd4c0.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular-8badfe75.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It-fc8b9304.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold-aa29a496.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'NanumBarunGothic';src:url("NanumBarunGothic-13b3dcba.ttf.woff2") format("woff2");font-display:swap;unicode-range:U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF;}*{box-sizing:border-box;}body{font:1rem/1.5 var(--font-family);margin:0;position:relative;overflow-wrap:break-word;overflow-wrap:anywhere;font-feature-settings:"kern","liga";background-color:var(--main-background-color);color:var(--main-color);}h1{font-size:1.5rem;}h2{font-size:1.375rem;}h3{font-size:1.25rem;}h1,h2,h3,h4,h5,h6{font-weight:500;}h1,h2,h3,h4{margin:25px 0 15px 0;padding-bottom:6px;}.docblock h3,.docblock h4,h5,h6{margin:15px 0 5px 0;}.docblock>h2:first-child,.docblock>h3:first-child,.docblock>h4:first-child,.docblock>h5:first-child,.docblock>h6:first-child{margin-top:0;}.main-heading h1{margin:0;padding:0;grid-area:main-heading-h1;overflow-wrap:break-word;overflow-wrap:anywhere;}.main-heading{position:relative;display:grid;grid-template-areas:"main-heading-breadcrumbs main-heading-breadcrumbs" "main-heading-h1 main-heading-toolbar" "main-heading-sub-heading main-heading-toolbar";grid-template-columns:minmax(105px,1fr) minmax(0,max-content);grid-template-rows:minmax(25px,min-content) min-content min-content;padding-bottom:6px;margin-bottom:15px;}.rustdoc-breadcrumbs{grid-area:main-heading-breadcrumbs;line-height:1.25;padding-top:5px;position:relative;z-index:1;}.rustdoc-breadcrumbs a{padding:5px 0 7px;}.content h2,.top-doc .docblock>h3,.top-doc .docblock>h4{border-bottom:1px solid var(--headings-border-bottom-color);}h1,h2{line-height:1.25;padding-top:3px;padding-bottom:9px;}h3.code-header{font-size:1.125rem;}h4.code-header{font-size:1rem;}.code-header{font-weight:600;margin:0;padding:0;white-space:pre-wrap;}.structfield,.sub-variant-field{margin:0.6em 0;}#crate-search,h1,h2,h3,h4,h5,h6,.sidebar,.mobile-topbar,.search-input,.search-results .result-name,.item-table dt>a,.out-of-band,.sub-heading,span.since,a.src,rustdoc-toolbar,summary.hideme,.scraped-example-list,.rustdoc-breadcrumbs,ul.all-items{font-family:"Fira Sans",Arial,NanumBarunGothic,sans-serif;}#toggle-all-docs,a.anchor,.section-header a,#src-sidebar a,.rust a,.sidebar h2 a,.sidebar h3 a,.mobile-topbar h2 a,h1 a,.search-results a,.search-results li,.stab,.result-name i{color:var(--main-color);}span.enum,a.enum,span.struct,a.struct,span.union,a.union,span.primitive,a.primitive,span.type,a.type,span.foreigntype,a.foreigntype{color:var(--type-link-color);}span.trait,a.trait,span.traitalias,a.traitalias{color:var(--trait-link-color);}span.associatedtype,a.associatedtype,span.constant,a.constant,span.static,a.static{color:var(--assoc-item-link-color);}span.fn,a.fn,span.method,a.method,span.tymethod,a.tymethod{color:var(--function-link-color);}span.attr,a.attr,span.derive,a.derive,span.macro,a.macro{color:var(--macro-link-color);}span.mod,a.mod{color:var(--mod-link-color);}span.keyword,a.keyword{color:var(--keyword-link-color);}a{color:var(--link-color);text-decoration:none;}ol,ul{padding-left:24px;}ul ul,ol ul,ul ol,ol ol{margin-bottom:.625em;}p,.docblock>.warning{margin:0 0 .75em 0;}p:last-child,.docblock>.warning:last-child{margin:0;}button{padding:1px 6px;cursor:pointer;}button#toggle-all-docs{padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.rustdoc{display:flex;flex-direction:row;flex-wrap:nowrap;}main{position:relative;flex-grow:1;padding:10px 15px 40px 45px;min-width:0;}.src main{padding:15px;}.width-limiter{max-width:960px;margin-right:auto;}details:not(.toggle) summary{margin-bottom:.6em;}code,pre,.code-header,.type-signature{font-family:var(--font-family-code);}.docblock code,.item-table dd code{border-radius:3px;padding:0 0.125em;}.docblock pre code,.item-table dd pre code{padding:0;}pre{padding:14px;line-height:1.5;}pre.item-decl{overflow-x:auto;}.item-decl .type-contents-toggle{contain:initial;}.src .content pre{padding:20px;}.rustdoc.src .example-wrap .src-line-numbers{padding:20px 0 20px 4px;}img{max-width:100%;}.logo-container{line-height:0;display:block;}.rust-logo{filter:var(--rust-logo-filter);}.sidebar{font-size:0.875rem;flex:0 0 var(--desktop-sidebar-width);width:var(--desktop-sidebar-width);overflow-y:scroll;overscroll-behavior:contain;position:sticky;height:100vh;top:0;left:0;z-index:var(--desktop-sidebar-z-index);}.rustdoc.src .sidebar{flex-basis:50px;width:50px;border-right:1px solid;overflow-x:hidden;overflow-y:hidden;}.hide-sidebar .sidebar,.hide-sidebar .sidebar-resizer{display:none;}.sidebar-resizer{touch-action:none;width:9px;cursor:col-resize;z-index:calc(var(--desktop-sidebar-z-index) + 1);position:fixed;height:100%;left:calc(var(--desktop-sidebar-width) + 1px);}.rustdoc.src .sidebar-resizer{left:49px;}.src-sidebar-expanded .src .sidebar-resizer{left:var(--src-sidebar-width);}.sidebar-resizing{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;}.sidebar-resizing*{cursor:col-resize !important;}.sidebar-resizing .sidebar{position:fixed;}.sidebar-resizing>body{padding-left:var(--resizing-sidebar-width);}.sidebar-resizer:hover,.sidebar-resizer:active,.sidebar-resizer:focus,.sidebar-resizer.active{width:10px;margin:0;left:var(--desktop-sidebar-width);border-left:solid 1px var(--sidebar-resizer-hover);}.src-sidebar-expanded .rustdoc.src .sidebar-resizer:hover,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:active,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,.src-sidebar-expanded .rustdoc.src .sidebar-resizer.active{left:calc(var(--src-sidebar-width) - 1px);}@media (pointer:coarse){.sidebar-resizer{display:none !important;}}.sidebar-resizer.active{padding:0 140px;width:2px;margin-left:-140px;border-left:none;}.sidebar-resizer.active:before{border-left:solid 2px var(--sidebar-resizer-active);display:block;height:100%;content:"";}.sidebar,.mobile-topbar,.sidebar-menu-toggle,#src-sidebar{background-color:var(--sidebar-background-color);}.src .sidebar>*{visibility:hidden;}.src-sidebar-expanded .src .sidebar{overflow-y:auto;flex-basis:var(--src-sidebar-width);width:var(--src-sidebar-width);}.src-sidebar-expanded .src .sidebar>*{visibility:visible;}#all-types{margin-top:1em;}*{scrollbar-width:initial;scrollbar-color:var(--scrollbar-color);}.sidebar{scrollbar-width:thin;scrollbar-color:var(--scrollbar-color);}::-webkit-scrollbar{width:12px;}.sidebar::-webkit-scrollbar{width:8px;}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0;background-color:var(--scrollbar-track-background-color);}.sidebar::-webkit-scrollbar-track{background-color:var(--scrollbar-track-background-color);}::-webkit-scrollbar-thumb,.sidebar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-background-color);}.hidden{display:none !important;}.logo-container>img{height:48px;width:48px;}ul.block,.block li,.block ul{padding:0;margin:0;list-style:none;}.block ul a{padding-left:1rem;}.sidebar-elems a,.sidebar>h2 a{display:block;padding:0.25rem;margin-right:0.25rem;border-left:solid var(--sidebar-elems-left-padding) transparent;margin-left:calc(-0.25rem - var(--sidebar-elems-left-padding));background-clip:border-box;}.hide-toc #rustdoc-toc,.hide-toc .in-crate{display:none;}.hide-modnav #rustdoc-modnav{display:none;}.sidebar h2{text-wrap:balance;overflow-wrap:anywhere;padding:0;margin:0.7rem 0;}.sidebar h3{text-wrap:balance;overflow-wrap:anywhere;font-size:1.125rem;padding:0;margin:0;}.sidebar-elems,.sidebar>.version,.sidebar>h2{padding-left:var(--sidebar-elems-left-padding);}.sidebar a{color:var(--sidebar-link-color);}.sidebar .current,.sidebar .current a,.sidebar-crate a.logo-container:hover+h2 a,.sidebar a:hover:not(.logo-container){background-color:var(--sidebar-current-link-background-color);}.sidebar-elems .block{margin-bottom:2em;}.sidebar-elems .block li a{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.sidebar-crate{display:flex;align-items:center;justify-content:center;margin:14px 32px 1rem;row-gap:10px;column-gap:32px;flex-wrap:wrap;}.sidebar-crate h2{flex-grow:1;margin:0 -8px;align-self:start;}.sidebar-crate .logo-container{margin:0 calc(-16px - var(--sidebar-elems-left-padding));padding:0 var(--sidebar-elems-left-padding);text-align:center;}.sidebar-crate .logo-container img{margin-top:-16px;border-top:solid 16px transparent;box-sizing:content-box;position:relative;background-clip:border-box;z-index:1;}.sidebar-crate h2 a{display:block;border-left:solid var(--sidebar-elems-left-padding) transparent;background-clip:border-box;margin:0 calc(-24px + 0.25rem) 0 calc(-0.2rem - var(--sidebar-elems-left-padding));padding:calc((16px - 0.57rem ) / 2 ) 0.25rem;padding-left:0.2rem;}.sidebar-crate h2 .version{display:block;font-weight:normal;font-size:1rem;overflow-wrap:break-word;}.sidebar-crate+.version{margin-top:-1rem;margin-bottom:1rem;}.mobile-topbar{display:none;}.rustdoc .example-wrap{display:flex;position:relative;margin-bottom:10px;}.rustdoc .example-wrap>pre,.rustdoc .scraped-example .src-line-numbers,.rustdoc .scraped-example .src-line-numbers>pre{border-radius:6px;}.rustdoc .example-wrap>.example-line-numbers,.rustdoc .scraped-example .src-line-numbers,.rustdoc .scraped-example .src-line-numbers>pre{border-top-right-radius:0;border-bottom-right-radius:0;}.rustdoc .example-wrap>.example-line-numbers+pre,.rustdoc .scraped-example .rust{border-top-left-radius:0;border-bottom-left-radius:0;}.rustdoc .scraped-example{position:relative;}.rustdoc .example-wrap:last-child{margin-bottom:0px;}.rustdoc .example-wrap pre{margin:0;flex-grow:1;}.scraped-example:not(.expanded) .example-wrap{max-height:calc(1.5em * 5 + 10px);}.more-scraped-examples .scraped-example:not(.expanded) .example-wrap{max-height:calc(1.5em * 10 + 10px);}.rustdoc:not(.src) .scraped-example:not(.expanded) .src-line-numbers,.rustdoc:not(.src) .scraped-example:not(.expanded) .src-line-numbers>pre,.rustdoc:not(.src) .scraped-example:not(.expanded) pre.rust{padding-bottom:0;overflow:auto hidden;}.rustdoc:not(.src) .scraped-example .src-line-numbers{padding-top:0;}.rustdoc:not(.src) .scraped-example.expanded .src-line-numbers{padding-bottom:0;}.rustdoc:not(.src) .example-wrap pre{overflow:auto;}.rustdoc .example-wrap pre.example-line-numbers,.rustdoc .example-wrap .src-line-numbers{min-width:fit-content;flex-grow:0;text-align:right;-webkit-user-select:none;user-select:none;padding:14px 8px;padding-right:2px;color:var(--src-line-numbers-span-color);}.rustdoc .scraped-example .example-wrap .src-line-numbers{padding:0;}.rustdoc .src-line-numbers pre{padding:14px 0;}.src-line-numbers a,.src-line-numbers span{color:var(--src-line-numbers-span-color);padding:0 8px;}.src-line-numbers :target{background-color:transparent;border-right:none;padding:0 8px;}.src-line-numbers .line-highlighted{background-color:var(--src-line-number-highlighted-background-color);}.search-loading{text-align:center;}.item-table dd{overflow-wrap:break-word;overflow-wrap:anywhere;}.docblock :not(pre)>code,.item-table dd code{white-space:pre-wrap;}.top-doc .docblock h2{font-size:1.375rem;}.top-doc .docblock h3{font-size:1.25rem;}.top-doc .docblock h4,.top-doc .docblock h5{font-size:1.125rem;}.top-doc .docblock h6{font-size:1rem;}.docblock h5{font-size:1rem;}.docblock h6{font-size:0.875rem;}.docblock{margin-left:var(--docblock-indent);position:relative;}.docblock>:not(.more-examples-toggle):not(.example-wrap){max-width:100%;overflow-x:auto;}.sub-heading{font-size:1rem;flex-grow:0;grid-area:main-heading-sub-heading;line-height:1.25;padding-bottom:4px;}.main-heading rustdoc-toolbar,.main-heading .out-of-band{grid-area:main-heading-toolbar;}rustdoc-toolbar{display:flex;flex-direction:row;flex-wrap:nowrap;min-height:60px;}.docblock code,.item-table dd code,pre,.rustdoc.src .example-wrap,.example-wrap .src-line-numbers{background-color:var(--code-block-background-color);border-radius:var(--code-block-border-radius);text-decoration:inherit;}#main-content{position:relative;}.docblock table{margin:.5em 0;border-collapse:collapse;}.docblock table td,.docblock table th{padding:.5em;border:1px solid var(--border-color);}.docblock table tbody tr:nth-child(2n){background:var(--table-alt-row-background-color);}.docblock .stab,.item-table dd .stab,.docblock p code{display:inline-block;}.docblock li{margin-bottom:.4em;}.docblock li p:not(:last-child){margin-bottom:.3em;}div.where{white-space:pre-wrap;font-size:0.875rem;}.item-info{display:block;margin-left:var(--docblock-indent);}.impl-items>.item-info{margin-left:calc(var(--docblock-indent) + var(--impl-items-indent));}.item-info code{font-size:0.875rem;}#main-content>.item-info{margin-left:0;}nav.sub{flex-grow:1;flex-flow:row nowrap;margin:4px 0 0 0;display:flex;align-items:center;}.search-form{position:relative;display:flex;height:34px;flex-grow:1;margin-bottom:4px;}.src nav.sub{margin:0 0 -10px 0;}.section-header{display:block;position:relative;}.section-header:hover>.anchor,.impl:hover>.anchor,.trait-impl:hover>.anchor,.variant:hover>.anchor{display:initial;}.anchor{display:none;position:absolute;left:-0.5em;background:none !important;}.anchor.field{left:-5px;}.section-header>.anchor{left:-15px;padding-right:8px;}h2.section-header>.anchor{padding-right:6px;}a.doc-anchor{color:var(--main-color);display:none;position:absolute;left:-17px;padding-right:10px;padding-left:3px;}*:hover>.doc-anchor{display:block;}.top-doc>.docblock>*:first-child>.doc-anchor{display:none !important;}.main-heading a:hover,.example-wrap .rust a:hover,.all-items a:hover,.docblock a:not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),.item-table dd a:not(.scrape-help):not(.tooltip):hover,.item-info a{text-decoration:underline;}.crate.block li.current a{font-weight:500;}table,.item-table{overflow-wrap:break-word;}.item-table{padding:0;margin:0;width:100%;}.item-table>dt{padding-right:1.25rem;}.item-table>dd{margin-inline-start:0;margin-left:0;}.search-results-title{margin-top:0;white-space:nowrap;display:flex;align-items:baseline;}.search-results-title+.sub-heading{color:var(--main-color);display:flex;align-items:baseline;white-space:nowrap;}#crate-search-div{position:relative;min-width:0;}#crate-search{padding:0 23px 0 4px;max-width:100%;text-overflow:ellipsis;border:1px solid var(--border-color);border-radius:4px;outline:none;cursor:pointer;-moz-appearance:none;-webkit-appearance:none;text-indent:0.01px;background-color:var(--main-background-color);color:inherit;line-height:1.5;font-weight:500;}#crate-search:hover,#crate-search:focus{border-color:var(--crate-search-hover-border);}#crate-search-div::after{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;content:"";background-repeat:no-repeat;background-size:20px;background-position:calc(100% - 2px) 56%;background-image:url('data:image/svg+xml, \ + ');filter:var(--crate-search-div-filter);}#crate-search-div:hover::after,#crate-search-div:focus-within::after{filter:var(--crate-search-div-hover-filter);}#crate-search>option{font-size:1rem;}.search-input{-webkit-appearance:none;outline:none;border:1px solid var(--border-color);border-radius:2px;padding:8px;font-size:1rem;flex-grow:1;background-color:var(--button-background-color);color:var(--search-color);}.search-input:focus{border-color:var(--search-input-focused-border-color);}.search-results{display:none;}.search-results.active{display:block;margin:0;padding:0;}.search-results>a{display:grid;grid-template-areas:"search-result-name search-result-desc" "search-result-type-signature search-result-type-signature";grid-template-columns:.6fr .4fr;margin-left:2px;margin-right:2px;border-bottom:1px solid var(--search-result-border-color);column-gap:1em;}.search-results>a>div.desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;grid-area:search-result-desc;}.search-results a:hover,.search-results a:focus{background-color:var(--search-result-link-focus-background-color);}.search-results .result-name{display:flex;align-items:center;justify-content:start;grid-area:search-result-name;}.search-results .result-name .alias{color:var(--search-results-alias-color);}.search-results .result-name .grey{color:var(--search-results-grey-color);}.search-results .result-name .typename{color:var(--search-results-grey-color);font-size:0.875rem;width:var(--search-typename-width);}.search-results .result-name .path{word-break:break-all;max-width:calc(100% - var(--search-typename-width));display:inline-block;}.search-results .result-name .path>*{display:inline;}.search-results .type-signature{grid-area:search-result-type-signature;white-space:pre-wrap;}.popover{position:absolute;top:100%;right:0;z-index:calc(var(--desktop-sidebar-z-index) + 1);margin-top:7px;border-radius:3px;border:1px solid var(--border-color);background-color:var(--main-background-color);color:var(--main-color);--popover-arrow-offset:11px;}.popover::before{content:'';position:absolute;right:var(--popover-arrow-offset);border:solid var(--border-color);border-width:1px 1px 0 0;background-color:var(--main-background-color);padding:4px;transform:rotate(-45deg);top:-5px;}.setting-line{margin:1.2em 0.6em;}.setting-radio input,.setting-check input{margin-right:0.3em;height:1.2rem;width:1.2rem;border:2px solid var(--settings-input-border-color);outline:none;-webkit-appearance:none;cursor:pointer;}.setting-radio input{border-radius:50%;}.setting-radio span,.setting-check span{padding-bottom:1px;}.setting-radio{margin-top:0.1em;margin-bottom:0.1em;min-width:3.8em;padding:0.3em;display:inline-flex;align-items:center;cursor:pointer;}.setting-radio+.setting-radio{margin-left:0.5em;}.setting-check{margin-right:20px;display:flex;align-items:center;cursor:pointer;}.setting-radio input:checked{box-shadow:inset 0 0 0 3px var(--main-background-color);background-color:var(--settings-input-color);}.setting-check input:checked{background-color:var(--settings-input-color);border-width:1px;content:url('data:image/svg+xml,\ + \ + ');}.setting-radio input:focus,.setting-check input:focus{box-shadow:0 0 1px 1px var(--settings-input-color);}.setting-radio input:checked:focus{box-shadow:inset 0 0 0 3px var(--main-background-color),0 0 2px 2px var(--settings-input-color);}.setting-radio input:hover,.setting-check input:hover{border-color:var(--settings-input-color) !important;}#settings.popover{--popover-arrow-offset:202px;top:calc(100% - 16px);}#help.popover{max-width:600px;--popover-arrow-offset:118px;top:calc(100% - 16px);}#help dt{float:left;clear:left;margin-right:0.5rem;}#help dd{margin-bottom:0.5rem;}#help span.top,#help span.bottom{text-align:center;display:block;font-size:1.125rem;padding:0 0.5rem;text-wrap-style:balance;}#help span.top{margin:10px 0;border-bottom:1px solid var(--border-color);padding-bottom:4px;margin-bottom:6px;}#help span.bottom{clear:both;border-top:1px solid var(--border-color);}.side-by-side{display:flex;margin-bottom:20px;}.side-by-side>div{width:50%;padding:0 20px 0 17px;}.item-info .stab{display:block;padding:3px;margin-bottom:5px;}.item-table dt .stab{margin-left:0.3125em;}.stab{padding:0 2px;font-size:0.875rem;font-weight:normal;color:var(--main-color);background-color:var(--stab-background-color);width:fit-content;white-space:pre-wrap;border-radius:3px;display:inline;vertical-align:baseline;}.stab.portability>code{background:none;color:var(--stab-code-color);}.stab .emoji,.item-info .stab::before{font-size:1.25rem;}.stab .emoji{margin-right:0.3rem;}.item-info .stab::before{content:"\0";width:0;display:inline-block;color:transparent;}.emoji{text-shadow:1px 0 0 black,-1px 0 0 black,0 1px 0 black,0 -1px 0 black;}.since{font-weight:normal;font-size:initial;}.rightside{padding-left:12px;float:right;}.rightside:not(a),.out-of-band,.sub-heading,rustdoc-toolbar{color:var(--right-side-color);}pre.rust{tab-size:4;-moz-tab-size:4;}pre.rust .kw{color:var(--code-highlight-kw-color);}pre.rust .kw-2{color:var(--code-highlight-kw-2-color);}pre.rust .lifetime{color:var(--code-highlight-lifetime-color);}pre.rust .prelude-ty{color:var(--code-highlight-prelude-color);}pre.rust .prelude-val{color:var(--code-highlight-prelude-val-color);}pre.rust .string{color:var(--code-highlight-string-color);}pre.rust .number{color:var(--code-highlight-number-color);}pre.rust .bool-val{color:var(--code-highlight-literal-color);}pre.rust .self{color:var(--code-highlight-self-color);}pre.rust .attr{color:var(--code-highlight-attribute-color);}pre.rust .macro,pre.rust .macro-nonterminal{color:var(--code-highlight-macro-color);}pre.rust .question-mark{font-weight:bold;color:var(--code-highlight-question-mark-color);}pre.rust .comment{color:var(--code-highlight-comment-color);}pre.rust .doccomment{color:var(--code-highlight-doc-comment-color);}.rustdoc.src .example-wrap pre.rust a{background:var(--codeblock-link-background);}.example-wrap.compile_fail,.example-wrap.should_panic{border-left:2px solid var(--codeblock-error-color);}.ignore.example-wrap{border-left:2px solid var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover,.example-wrap.should_panic:hover{border-left:2px solid var(--codeblock-error-hover-color);}.example-wrap.ignore:hover{border-left:2px solid var(--codeblock-ignore-hover-color);}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip{color:var(--codeblock-error-color);}.example-wrap.ignore .tooltip{color:var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover .tooltip,.example-wrap.should_panic:hover .tooltip{color:var(--codeblock-error-hover-color);}.example-wrap.ignore:hover .tooltip{color:var(--codeblock-ignore-hover-color);}.example-wrap .tooltip{position:absolute;display:block;left:-25px;top:5px;margin:0;line-height:1;}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip,.example-wrap.ignore .tooltip{font-weight:bold;font-size:1.25rem;}.content .docblock .warning{border-left:2px solid var(--warning-border-color);padding:14px;position:relative;overflow-x:visible !important;}.content .docblock .warning::before{color:var(--warning-border-color);content:"ⓘ";position:absolute;left:-25px;top:5px;font-weight:bold;font-size:1.25rem;}.top-doc>.docblock>.warning:first-child::before{top:20px;}.example-wrap>a.test-arrow,.example-wrap .button-holder{visibility:hidden;position:absolute;top:4px;right:4px;z-index:1;}a.test-arrow{height:var(--copy-path-height);padding:6px 4px 0 11px;}a.test-arrow::before{content:url('data:image/svg+xml,');}.example-wrap .button-holder{display:flex;}@media not (pointer:coarse){.example-wrap:hover>a.test-arrow,.example-wrap:hover>.button-holder{visibility:visible;}}.example-wrap .button-holder.keep-visible{visibility:visible;}.example-wrap .button-holder>*{background:var(--main-background-color);cursor:pointer;border-radius:var(--button-border-radius);height:var(--copy-path-height);width:var(--copy-path-width);border:0;color:var(--code-example-button-color);}.example-wrap .button-holder>*:hover{color:var(--code-example-button-hover-color);}.example-wrap .button-holder>*:not(:first-child){margin-left:var(--button-left-margin);}.example-wrap .button-holder .copy-button{padding:2px 0 0 4px;}.example-wrap .button-holder .copy-button::before,.example-wrap .test-arrow::before{filter:var(--copy-path-img-filter);}.example-wrap .button-holder .copy-button::before{content:var(--clipboard-image);}.example-wrap .button-holder .copy-button:hover::before,.example-wrap .test-arrow:hover::before{filter:var(--copy-path-img-hover-filter);}.example-wrap .button-holder .copy-button.clicked::before{content:var(--checkmark-image);padding-right:5px;}.code-attribute{font-weight:300;color:var(--code-attribute-color);}.item-spacer{width:100%;height:12px;display:block;}.main-heading span.since::before{content:"Since ";}.sub-variant h4{font-size:1rem;font-weight:400;margin-top:0;margin-bottom:0;}.sub-variant{margin-left:24px;margin-bottom:40px;}.sub-variant>.sub-variant-field{margin-left:24px;}@keyframes targetfadein{from{background-color:var(--main-background-color);}10%{background-color:var(--target-border-color);}to{background-color:var(--target-background-color);}}:target{padding-right:3px;background-color:var(--target-background-color);border-right:3px solid var(--target-border-color);}.code-header a.tooltip{color:inherit;margin-right:15px;position:relative;}.code-header a.tooltip:hover{color:var(--link-color);}a.tooltip:hover::after{position:absolute;top:calc(100% - 10px);left:-15px;right:-15px;height:20px;content:"\00a0";}@media not (prefers-reduced-motion){:target{animation:0.65s cubic-bezier(0,0,0.1,1.0) 0.1s targetfadein;}.fade-out{opacity:0;transition:opacity 0.45s cubic-bezier(0,0,0.1,1.0);}}.popover.tooltip .content{margin:0.25em 0.5em;}.popover.tooltip .content pre,.popover.tooltip .content code{background:transparent;margin:0;padding:0;font-size:1.25rem;white-space:pre-wrap;}.popover.tooltip .content>h3:first-child{margin:0 0 5px 0;}.search-failed{text-align:center;margin-top:20px;display:none;}.search-failed.active{display:block;}.search-failed>ul{text-align:left;max-width:570px;margin-left:auto;margin-right:auto;}#search-tabs{margin-top:0.25rem;display:flex;flex-direction:row;gap:1px;margin-bottom:4px;}#search-tabs button{text-align:center;font-size:1.125rem;border:0;border-top:2px solid;flex:1;line-height:1.5;color:inherit;}#search-tabs button:not(.selected){background-color:var(--search-tab-button-not-selected-background);border-top-color:var(--search-tab-button-not-selected-border-top-color);}#search-tabs button:hover,#search-tabs button.selected{background-color:var(--search-tab-button-selected-background);border-top-color:var(--search-tab-button-selected-border-top-color);}#search-tabs .count{font-size:1rem;font-variant-numeric:tabular-nums;color:var(--search-tab-title-count-color);}#search .error code{border-radius:3px;background-color:var(--search-error-code-background-color);}.search-corrections{font-weight:normal;}#src-sidebar{width:100%;overflow:auto;}#src-sidebar div.files>a:hover,details.dir-entry summary:hover,#src-sidebar div.files>a:focus,details.dir-entry summary:focus{background-color:var(--src-sidebar-background-hover);}#src-sidebar div.files>a.selected{background-color:var(--src-sidebar-background-selected);}.src-sidebar-title{position:sticky;top:0;display:flex;padding:8px 8px 0 48px;margin-bottom:7px;background:var(--sidebar-background-color);border-bottom:1px solid var(--border-color);}#settings-menu,#help-button,button#toggle-all-docs{margin-left:var(--button-left-margin);display:flex;line-height:1.25;min-width:14px;}#sidebar-button{display:none;line-height:0;}.hide-sidebar #sidebar-button,.src #sidebar-button{display:flex;margin-right:4px;position:fixed;left:6px;height:34px;width:34px;background-color:var(--main-background-color);z-index:1;}.src #sidebar-button{left:8px;z-index:calc(var(--desktop-sidebar-z-index) + 1);}.hide-sidebar .src #sidebar-button{position:static;}#settings-menu>a,#help-button>a,#sidebar-button>a,button#toggle-all-docs{display:flex;align-items:center;justify-content:center;flex-direction:column;border:1px solid transparent;border-radius:var(--button-border-radius);color:var(--main-color);}#settings-menu>a,#help-button>a,button#toggle-all-docs{width:80px;border-radius:var(--toolbar-button-border-radius);}#settings-menu>a,#help-button>a{min-width:0;}#sidebar-button>a{background-color:var(--button-background-color);border-color:var(--border-color);width:33px;}#settings-menu>a:hover,#settings-menu>a:focus-visible,#help-button>a:hover,#help-button>a:focus-visible,#sidebar-button>a:hover,#sidebar-button>a:focus-visible,button#toggle-all-docs:hover,button#toggle-all-docs:focus-visible{border-color:var(--settings-button-border-focus);text-decoration:none;}#settings-menu>a:before{content:url('data:image/svg+xml,\ + ');width:18px;height:18px;filter:var(--settings-menu-filter);}button#toggle-all-docs:before{content:url('data:image/svg+xml,\ + ');width:18px;height:18px;filter:var(--settings-menu-filter);}#help-button>a:before{content:url('data:image/svg+xml,\ + \ + ?');width:18px;height:18px;filter:var(--settings-menu-filter);}button#toggle-all-docs:before,#help-button>a:before,#settings-menu>a:before{filter:var(--settings-menu-filter);margin:8px;}@media not (pointer:coarse){button#toggle-all-docs:hover:before,#help-button>a:hover:before,#settings-menu>a:hover:before{filter:var(--settings-menu-hover-filter);}}button[disabled]#toggle-all-docs{opacity:0.25;border:solid 1px var(--main-background-color);background-size:cover;}button[disabled]#toggle-all-docs:hover{border:solid 1px var(--main-background-color);cursor:not-allowed;}rustdoc-toolbar span.label{font-size:1rem;flex-grow:1;padding-bottom:4px;}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}#copy-path{color:var(--copy-path-button-color);background:var(--main-background-color);height:var(--copy-path-height);width:var(--copy-path-width);margin-left:10px;padding:0;padding-left:2px;border:0;font-size:0;}#copy-path::before{filter:var(--copy-path-img-filter);content:var(--clipboard-image);}#copy-path:hover::before{filter:var(--copy-path-img-hover-filter);}#copy-path.clicked::before{content:var(--checkmark-image);}@keyframes rotating{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}#settings-menu.rotate>a img{animation:rotating 2s linear infinite;}kbd{display:inline-block;padding:3px 5px;font:15px monospace;line-height:10px;vertical-align:middle;border:solid 1px var(--border-color);border-radius:3px;color:var(--kbd-color);background-color:var(--kbd-background);box-shadow:inset 0 -1px 0 var(--kbd-box-shadow-color);}ul.all-items>li{list-style:none;}details.dir-entry{padding-left:4px;}details.dir-entry>summary{margin:0 0 0 -4px;padding:0 0 0 4px;cursor:pointer;}details.dir-entry div.folders,details.dir-entry div.files{padding-left:23px;}details.dir-entry a{display:block;}details.toggle{contain:layout;position:relative;}details.big-toggle{contain:inline-size;}details.toggle>summary.hideme{cursor:pointer;font-size:1rem;}details.toggle>summary{list-style:none;outline:none;}details.toggle>summary::-webkit-details-marker,details.toggle>summary::marker{display:none;}details.toggle>summary.hideme>span{margin-left:9px;}details.toggle>summary::before{background:url('data:image/svg+xml,\ + ');content:"";cursor:pointer;width:16px;height:16px;display:inline-block;vertical-align:middle;opacity:.5;filter:var(--toggle-filter);}details.toggle>summary.hideme>span,.more-examples-toggle summary,.more-examples-toggle .hide-more{color:var(--toggles-color);}details.toggle>summary::after{content:"Expand";overflow:hidden;width:0;height:0;position:absolute;}details.toggle>summary.hideme::after{content:"";}details.toggle>summary:focus::before,details.toggle>summary:hover::before{opacity:1;}details.toggle>summary:focus-visible::before{outline:1px dotted #000;outline-offset:1px;}details.non-exhaustive{margin-bottom:8px;}details.toggle>summary.hideme::before{position:relative;}details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;top:4px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>.methods>details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;}.impl-items>*:not(.item-info),.implementors-toggle>.docblock,#main-content>.methods>:not(.item-info){margin-left:var(--impl-items-indent);}details.big-toggle>summary:not(.hideme)::before{left:-34px;top:9px;}details.toggle[open] >summary.hideme{position:absolute;}details.toggle[open] >summary.hideme>span{display:none;}details.toggle[open] >summary::before{background:url('data:image/svg+xml,\ + ');}details.toggle[open] >summary::after{content:"Collapse";}details.toggle:not([open])>summary .docblock{max-height:calc(1.5em + 0.75em);overflow-y:hidden;}details.toggle:not([open])>summary .docblock>:first-child{max-width:100%;overflow:hidden;width:fit-content;white-space:nowrap;position:relative;padding-right:1em;}details.toggle:not([open])>summary .docblock>:first-child::after{content:"…";position:absolute;right:0;top:0;bottom:0;z-index:1;background-color:var(--main-background-color);font:1rem/1.5 "Source Serif 4",NanumBarunGothic,serif;padding-left:0.2em;}details.toggle:not([open])>summary .docblock>div:first-child::after{padding-top:calc(1.5em + 0.75em - 1.2rem);}details.toggle>summary .docblock{margin-top:0.75em;}.docblock summary>*{display:inline-block;}.docblock>.example-wrap:first-child .tooltip{margin-top:16px;}.src #sidebar-button>a:before,.sidebar-menu-toggle:before{content:url('data:image/svg+xml,\ + ');opacity:0.75;}.sidebar-menu-toggle:hover:before,.sidebar-menu-toggle:active:before,.sidebar-menu-toggle:focus:before{opacity:1;}.src #sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');opacity:0.75;}@media (max-width:850px){#search-tabs .count{display:block;}.side-by-side{flex-direction:column-reverse;}.side-by-side>div{width:auto;}}@media (max-width:700px){:root{--impl-items-indent:0.7em;}*[id]{scroll-margin-top:45px;}#copy-path{width:0;visibility:hidden;}rustdoc-toolbar span.label{display:none;}#settings-menu>a,#help-button>a,button#toggle-all-docs{width:33px;}#settings.popover{--popover-arrow-offset:86px;}#help.popover{--popover-arrow-offset:48px;}.rustdoc{display:block;}main{padding-left:15px;padding-top:0px;}.sidebar .logo-container,.sidebar .location,.sidebar-resizer{display:none;}.sidebar{position:fixed;top:45px;left:-1000px;z-index:11;height:calc(100vh - 45px);width:200px;}.src main,.rustdoc.src .sidebar{top:0;padding:0;height:100vh;border:0;}.src .search-form{margin-left:40px;}.src .main-heading{margin-left:8px;}.hide-sidebar .search-form{margin-left:32px;}.hide-sidebar .src .search-form{margin-left:0;}.sidebar.shown,.src-sidebar-expanded .src .sidebar,.rustdoc:not(.src) .sidebar:focus-within{left:0;}.mobile-topbar h2{padding-bottom:0;margin:auto 0.5em auto auto;overflow:hidden;font-size:24px;white-space:nowrap;text-overflow:ellipsis;}.mobile-topbar .logo-container>img{max-width:35px;max-height:35px;margin:5px 0 5px 20px;}.mobile-topbar{display:flex;flex-direction:row;position:sticky;z-index:10;font-size:2rem;height:45px;width:100%;left:0;top:0;}.hide-sidebar .mobile-topbar{display:none;}.sidebar-menu-toggle{width:45px;border:none;line-height:0;}.hide-sidebar .sidebar-menu-toggle{display:none;}.sidebar-elems{margin-top:1em;}.anchor{display:none !important;}#main-content>details.toggle>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}.sidebar-menu-toggle:before{filter:var(--mobile-sidebar-menu-filter);}.sidebar-menu-toggle:hover{background:var(--main-background-color);}.search-results>a,.search-results>a>div{display:block;}.search-results>a{padding:5px 0px;}.search-results>a>div.desc,.item-table dd{padding-left:2em;}.search-results .result-name{display:block;}.search-results .result-name .typename{width:initial;margin-right:0;}.search-results .result-name .typename,.search-results .result-name .path{display:inline;}.src-sidebar-expanded .src .sidebar{position:fixed;max-width:100vw;width:100vw;}.src .src-sidebar-title{padding-top:0;}details.implementors-toggle:not(.top-doc)>summary{margin-left:10px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>.methods>details.toggle>summary:not(.hideme)::before{left:-20px;}summary>.item-info{margin-left:10px;}.impl-items>.item-info{margin-left:calc(var(--impl-items-indent) + 10px);}.src nav.sub{margin:0 0 -25px 0;padding:var(--nav-sub-mobile-padding);}}@media (min-width:701px){.scraped-example-title{position:absolute;z-index:10;background:var(--main-background-color);bottom:8px;right:5px;padding:2px 4px;box-shadow:0 0 4px var(--main-background-color);}.item-table:not(.reexports){display:grid;grid-template-columns:33% 67%;}.item-table>dt,.item-table>dd{overflow-wrap:anywhere;}.item-table>dt{grid-column-start:1;}.item-table>dd{grid-column-start:2;}}@media print{:root{--docblock-indent:0;}nav.sidebar,nav.sub,.out-of-band,a.src,#copy-path,details.toggle[open] >summary::before,details.toggle>summary::before,details.toggle.top-doc>summary{display:none;}main{padding:10px;}}@media (max-width:464px){:root{--docblock-indent:12px;}.docblock code{overflow-wrap:break-word;overflow-wrap:anywhere;}nav.sub{flex-direction:column;}.search-form{align-self:stretch;}}.variant,.implementors-toggle>summary,.impl,#implementors-list>.docblock,.impl-items>section,.impl-items>.toggle>summary,.methods>section,.methods>.toggle>summary{margin-bottom:0.75em;}.variants>.docblock,.implementors-toggle>.docblock,.impl-items>.toggle[open]:not(:last-child),.methods>.toggle[open]:not(:last-child),.implementors-toggle[open]:not(:last-child){margin-bottom:2em;}#trait-implementations-list .impl-items>.toggle:not(:last-child),#synthetic-implementations-list .impl-items>.toggle:not(:last-child),#blanket-implementations-list .impl-items>.toggle:not(:last-child){margin-bottom:1em;}.scraped-example-list .scrape-help{margin-left:10px;padding:0 4px;font-weight:normal;font-size:12px;position:relative;bottom:1px;border:1px solid var(--scrape-example-help-border-color);border-radius:50px;color:var(--scrape-example-help-color);}.scraped-example-list .scrape-help:hover{border-color:var(--scrape-example-help-hover-border-color);color:var(--scrape-example-help-hover-color);}.scraped-example:not(.expanded) .example-wrap::before,.scraped-example:not(.expanded) .example-wrap::after{content:" ";width:100%;height:5px;position:absolute;z-index:1;}.scraped-example:not(.expanded) .example-wrap::before{top:0;background:linear-gradient(to bottom,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded) .example-wrap::after{bottom:0;background:linear-gradient(to top,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded){width:100%;overflow-y:hidden;margin-bottom:0;}.scraped-example:not(.expanded){overflow-x:hidden;}.scraped-example .rust span.highlight{background:var(--scrape-example-code-line-highlight);}.scraped-example .rust span.highlight.focus{background:var(--scrape-example-code-line-highlight-focus);}.more-examples-toggle{max-width:calc(100% + 25px);margin-top:10px;margin-left:-25px;}.more-examples-toggle .hide-more{margin-left:25px;cursor:pointer;}.more-scraped-examples{margin-left:25px;position:relative;}.toggle-line{position:absolute;top:5px;bottom:0;right:calc(100% + 10px);padding:0 4px;cursor:pointer;}.toggle-line-inner{min-width:2px;height:100%;background:var(--scrape-example-toggle-line-background);}.toggle-line:hover .toggle-line-inner{background:var(--scrape-example-toggle-line-hover-background);}.more-scraped-examples .scraped-example,.example-links{margin-top:20px;}.more-scraped-examples .scraped-example:first-child{margin-top:5px;}.example-links ul{margin-bottom:0;}:root[data-theme="light"],:root:not([data-theme]){--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--code-example-button-color:#7f7f7f;--code-example-button-hover-color:#595959;--settings-menu-filter:invert(50%);--settings-menu-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="dark"]{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--code-example-button-color:#7f7f7f;--code-example-button-hover-color:#a5a5a5;--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--settings-menu-filter:invert(50%);--settings-menu-hover-filter:invert(65%);--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="ayu"]{--main-background-color:#0f1419;--main-color:#c5c5c5;--settings-input-color:#ffb454;--settings-input-border-color:#999;--settings-button-color:#fff;--settings-button-border-focus:#e0e0e0;--sidebar-background-color:#14191f;--sidebar-background-color-hover:rgba(70,70,70,0.33);--code-block-background-color:#191f26;--scrollbar-track-background-color:transparent;--scrollbar-thumb-background-color:#5c6773;--scrollbar-color:#5c6773 #24292f;--headings-border-bottom-color:#5c6773;--border-color:#5c6773;--button-background-color:#141920;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#5c6773;--copy-path-button-color:#fff;--copy-path-img-filter:invert(70%);--copy-path-img-hover-filter:invert(100%);--code-example-button-color:#b2b2b2;--code-example-button-hover-color:#fff;--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ffa0a5;--trait-link-color:#39afd7;--assoc-item-link-color:#39afd7;--function-link-color:#fdd687;--macro-link-color:#a37acc;--keyword-link-color:#39afd7;--mod-link-color:#39afd7;--link-color:#39afd7;--sidebar-link-color:#53b1db;--sidebar-current-link-background-color:transparent;--search-result-link-focus-background-color:#3c3c3c;--search-result-border-color:#aaa3;--search-color:#fff;--search-error-code-background-color:#4f4c4c;--search-results-alias-color:#c5c5c5;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:none;--search-tab-button-not-selected-background:transparent !important;--search-tab-button-selected-border-top-color:none;--search-tab-button-selected-background:#141920 !important;--settings-menu-filter:invert(70%);--settings-menu-hover-filter:invert(100%);--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ff7733;--code-highlight-kw-2-color:#ff7733;--code-highlight-lifetime-color:#ff7733;--code-highlight-prelude-color:#69f2df;--code-highlight-prelude-val-color:#ff7733;--code-highlight-number-color:#b8cc52;--code-highlight-string-color:#b8cc52;--code-highlight-literal-color:#ff7733;--code-highlight-attribute-color:#e6e1cf;--code-highlight-self-color:#36a3d9;--code-highlight-macro-color:#a37acc;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#788797;--code-highlight-doc-comment-color:#a1ac88;--src-line-numbers-span-color:#5c6773;--src-line-number-highlighted-background-color:rgba(255,236,164,0.06);--target-background-color:rgba(255,236,164,0.06);--target-border-color:rgba(255,180,76,0.85);--kbd-color:#c5c5c5;--kbd-background:#314559;--kbd-box-shadow-color:#5c6773;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);--crate-search-div-hover-filter:invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);--crate-search-hover-border:#e0e0e0;--src-sidebar-background-selected:#14191f;--src-sidebar-background-hover:#14191f;--table-alt-row-background-color:#191f26;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(15,20,25,1);--scrape-example-code-wrapper-background-end:rgba(15,20,25,0);--sidebar-resizer-hover:hsl(34,50%,33%);--sidebar-resizer-active:hsl(34,100%,66%);}:root[data-theme="ayu"] h1,:root[data-theme="ayu"] h2,:root[data-theme="ayu"] h3,:root[data-theme="ayu"] h4,:where(:root[data-theme="ayu"]) h1 a,:root[data-theme="ayu"] .sidebar h2 a,:root[data-theme="ayu"] .sidebar h3 a{color:#fff;}:root[data-theme="ayu"] .docblock code{color:#ffb454;}:root[data-theme="ayu"] .docblock a>code{color:#39AFD7 !important;}:root[data-theme="ayu"] .code-header,:root[data-theme="ayu"] .docblock pre>code,:root[data-theme="ayu"] pre,:root[data-theme="ayu"] pre>code,:root[data-theme="ayu"] .item-info code,:root[data-theme="ayu"] .rustdoc.source .example-wrap{color:#e6e1cf;}:root[data-theme="ayu"] .sidebar .current,:root[data-theme="ayu"] .sidebar .current a,:root[data-theme="ayu"] .sidebar a:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:hover,:root[data-theme="ayu"] details.dir-entry summary:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:focus,:root[data-theme="ayu"] details.dir-entry summary:focus,:root[data-theme="ayu"] #src-sidebar div.files>a.selected{color:#ffb44c;}:root[data-theme="ayu"] .sidebar-elems .location{color:#ff7733;}:root[data-theme="ayu"] .src-line-numbers .line-highlighted{color:#708090;padding-right:7px;border-right:1px solid #ffb44c;}:root[data-theme="ayu"] .search-results a:hover,:root[data-theme="ayu"] .search-results a:focus{color:#fff !important;background-color:#3c3c3c;}:root[data-theme="ayu"] .search-results a{color:#0096cf;}:root[data-theme="ayu"] .search-results a div.desc{color:#c5c5c5;}:root[data-theme="ayu"] .result-name .primitive>i,:root[data-theme="ayu"] .result-name .keyword>i{color:#788797;}:root[data-theme="ayu"] #search-tabs>button.selected{border-bottom:1px solid #ffb44c !important;border-top:none;}:root[data-theme="ayu"] #search-tabs>button:not(.selected){border:none;background-color:transparent !important;}:root[data-theme="ayu"] #search-tabs>button:hover{border-bottom:1px solid rgba(242,151,24,0.3);}:root[data-theme="ayu"] #settings-menu>a img,:root[data-theme="ayu"] #sidebar-button>a:before{filter:invert(100);} \ No newline at end of file diff --git a/crates/doc/static.files/scrape-examples-2ea993ec.js b/crates/doc/static.files/scrape-examples-2ea993ec.js new file mode 100644 index 000000000000..87b6065d8138 --- /dev/null +++ b/crates/doc/static.files/scrape-examples-2ea993ec.js @@ -0,0 +1 @@ +"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelector(".src-line-numbers > pre");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines.children[line].offsetTop}else{const halfHeight=elt.offsetHeight/2;const offsetTop=lines.children[loc[0]].offsetTop;const lastLine=lines.children[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight}lines.parentElement.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset)}function createScrapeButton(parent,className,content){const button=document.createElement("button");button.className=className;button.innerText=content;parent.insertBefore(button,parent.firstChild);return button}window.updateScrapedExample=(example,buttonHolder)=>{let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");let expandButton=null;if(!example.classList.contains("expanded")){expandButton=createScrapeButton(buttonHolder,"expand","↕")}const isHidden=example.parentElement.classList.contains("more-scraped-examples");const locs=example.locs;if(locs.length>1){const next=createScrapeButton(buttonHolder,"next","≻");const prev=createScrapeButton(buttonHolder,"prev","≺");const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title};prev.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length})});next.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length})})}if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");scrollToLoc(example,locs[0][0],isHidden)}else{addClass(example,"expanded")}})}};function setupLoc(example,isHidden){example.locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);scrollToLoc(example,example.locs[0][0],isHidden)}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>setupLoc(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false})});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>setupLoc(el,true))})},{once:true})})})() \ No newline at end of file diff --git a/crates/doc/static.files/search-581efc7a.js b/crates/doc/static.files/search-581efc7a.js new file mode 100644 index 000000000000..06120fb615e9 --- /dev/null +++ b/crates/doc/static.files/search-581efc7a.js @@ -0,0 +1,6 @@ +"use strict";if(!Array.prototype.toSpliced){Array.prototype.toSpliced=function(){const me=this.slice();Array.prototype.splice.apply(me,arguments);return me}}function onEachBtwn(arr,func,funcBtwn){let skipped=true;for(const value of arr){if(!skipped){funcBtwn(value)}skipped=func(value)}}const itemTypes=["keyword","primitive","mod","externcrate","import","struct","enum","fn","type","static","trait","impl","tymethod","method","structfield","variant","macro","associatedtype","constant","associatedconstant","union","foreigntype","existential","attr","derive","traitalias","generic",];const TY_PRIMITIVE=itemTypes.indexOf("primitive");const TY_GENERIC=itemTypes.indexOf("generic");const TY_IMPORT=itemTypes.indexOf("import");const TY_TRAIT=itemTypes.indexOf("trait");const TY_FN=itemTypes.indexOf("fn");const TY_METHOD=itemTypes.indexOf("method");const TY_TYMETHOD=itemTypes.indexOf("tymethod");const ROOT_PATH=typeof window!=="undefined"?window.rootPath:"../";const UNBOXING_LIMIT=5;const REGEX_IDENT=/\p{ID_Start}\p{ID_Continue}*|_\p{ID_Continue}+/uy;const REGEX_INVALID_TYPE_FILTER=/[^a-z]/ui;const MAX_RESULTS=200;const NO_TYPE_FILTER=-1;const editDistanceState={current:[],prev:[],prevPrev:[],calculate:function calculate(a,b,limit){if(a.lengthlimit){return limit+1}while(b.length>0&&b[0]===a[0]){a=a.substring(1);b=b.substring(1)}while(b.length>0&&b[b.length-1]===a[a.length-1]){a=a.substring(0,a.length-1);b=b.substring(0,b.length-1)}if(b.length===0){return minDist}const aLength=a.length;const bLength=b.length;for(let i=0;i<=bLength;++i){this.current[i]=0;this.prev[i]=i;this.prevPrev[i]=Number.MAX_VALUE}for(let i=1;i<=aLength;++i){this.current[0]=i;const aIdx=i-1;for(let j=1;j<=bLength;++j){const bIdx=j-1;const substitutionCost=a[aIdx]===b[bIdx]?0:1;this.current[j]=Math.min(this.prev[j]+1,this.current[j-1]+1,this.prev[j-1]+substitutionCost,);if((i>1)&&(j>1)&&(a[aIdx]===b[bIdx-1])&&(a[aIdx-1]===b[bIdx])){this.current[j]=Math.min(this.current[j],this.prevPrev[j-2]+1,)}}const prevPrevTmp=this.prevPrev;this.prevPrev=this.prev;this.prev=this.current;this.current=prevPrevTmp}const distance=this.prev[bLength];return distance<=limit?distance:(limit+1)},};function editDistance(a,b,limit){return editDistanceState.calculate(a,b,limit)}function isEndCharacter(c){return"=,>-])".indexOf(c)!==-1}function isFnLikeTy(ty){return ty===TY_FN||ty===TY_METHOD||ty===TY_TYMETHOD}function isSeparatorCharacter(c){return c===","||c==="="}function isReturnArrow(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="->"}function skipWhitespace(parserState){while(parserState.pos0){const c=parserState.userQuery[pos-1];if(c===lookingFor){return true}else if(c!==" "){break}pos-=1}return false}function isLastElemGeneric(elems,parserState){return(elems.length>0&&elems[elems.length-1].generics.length>0)||prevIs(parserState,">")}function getFilteredNextElem(query,parserState,elems,isInGenerics){const start=parserState.pos;if(parserState.userQuery[parserState.pos]===":"&&!isPathStart(parserState)){throw["Expected type filter before ",":"]}getNextElem(query,parserState,elems,isInGenerics);if(parserState.userQuery[parserState.pos]===":"&&!isPathStart(parserState)){if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}if(elems.length===0){throw["Expected type filter before ",":"]}else if(query.literalSearch){throw["Cannot use quotes on type filter"]}const typeFilterElem=elems.pop();checkExtraTypeFilterCharacters(start,parserState);parserState.typeFilter=typeFilterElem.normalizedPathLast;parserState.pos+=1;parserState.totalElems-=1;query.literalSearch=false;getNextElem(query,parserState,elems,isInGenerics)}}function getItemsBefore(query,parserState,elems,endChar){let foundStopChar=true;let foundSeparator=false;const oldTypeFilter=parserState.typeFilter;parserState.typeFilter=null;const oldIsInBinding=parserState.isInBinding;parserState.isInBinding=null;let hofParameters=null;let extra="";if(endChar===">"){extra="<"}else if(endChar==="]"){extra="["}else if(endChar===")"){extra="("}else if(endChar===""){extra="->"}else{extra=endChar}while(parserState.pos"," after ","="]}hofParameters=[...elems];elems.length=0;parserState.pos+=2;foundStopChar=true;foundSeparator=false;continue}else if(c===" "){parserState.pos+=1;continue}else if(isSeparatorCharacter(c)){parserState.pos+=1;foundStopChar=true;foundSeparator=true;continue}else if(c===":"&&isPathStart(parserState)){throw["Unexpected ","::",": paths cannot start with ","::"]}else if(isEndCharacter(c)){throw["Unexpected ",c," after ",extra]}if(!foundStopChar){let extra=[];if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(endChar!==""){throw["Expected ",",",", ","=",", or ",endChar,...extra,", found ",c,]}throw["Expected ",","," or ","=",...extra,", found ",c,]}const posBefore=parserState.pos;getFilteredNextElem(query,parserState,elems,endChar!=="");if(endChar!==""&&parserState.pos>=parserState.length){throw["Unclosed ",extra]}if(posBefore===parserState.pos){parserState.pos+=1}foundStopChar=false}if(parserState.pos>=parserState.length&&endChar!==""){throw["Unclosed ",extra]}parserState.pos+=1;if(hofParameters){foundSeparator=false;if([...elems,...hofParameters].some(x=>x.bindingName)||parserState.isInBinding){throw["Unexpected ","="," within ","->"]}const hofElem=makePrimitiveElement("->",{generics:hofParameters,bindings:new Map([["output",[...elems]]]),typeFilter:null,});elems.length=0;elems[0]=hofElem}parserState.typeFilter=oldTypeFilter;parserState.isInBinding=oldIsInBinding;return{foundSeparator}}function getNextElem(query,parserState,elems,isInGenerics){const generics=[];skipWhitespace(parserState);let start=parserState.pos;let end;if("[(".indexOf(parserState.userQuery[parserState.pos])!==-1){let endChar=")";let name="()";let friendlyName="tuple";if(parserState.userQuery[parserState.pos]==="["){endChar="]";name="[]";friendlyName="slice"}parserState.pos+=1;const{foundSeparator}=getItemsBefore(query,parserState,generics,endChar);const typeFilter=parserState.typeFilter;const bindingName=parserState.isInBinding;parserState.typeFilter=null;parserState.isInBinding=null;for(const gen of generics){if(gen.bindingName!==null){throw["Type parameter ","=",` cannot be within ${friendlyName} `,name]}}if(name==="()"&&!foundSeparator&&generics.length===1&&typeFilter===null){elems.push(generics[0])}else if(name==="()"&&generics.length===1&&generics[0].name==="->"){generics[0].typeFilter=typeFilter;elems.push(generics[0])}else{if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive ",name," and ",typeFilter," both specified",]}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}elems.push(makePrimitiveElement(name,{bindingName,generics}))}}else if(parserState.userQuery[parserState.pos]==="&"){if(parserState.typeFilter!==null&&parserState.typeFilter!=="primitive"){throw["Invalid search type: primitive ","&"," and ",parserState.typeFilter," both specified",]}parserState.typeFilter=null;parserState.pos+=1;let c=parserState.userQuery[parserState.pos];while(c===" "&&parserState.pos=end){throw["Found generics without a path"]}parserState.pos+=1;getItemsBefore(query,parserState,generics,">")}else if(parserState.pos=end){throw["Found generics without a path"]}if(parserState.isInBinding){throw["Unexpected ","("," after ","="]}parserState.pos+=1;const typeFilter=parserState.typeFilter;parserState.typeFilter=null;getItemsBefore(query,parserState,generics,")");skipWhitespace(parserState);if(isReturnArrow(parserState)){parserState.pos+=2;skipWhitespace(parserState);getFilteredNextElem(query,parserState,generics,isInGenerics);generics[generics.length-1].bindingName=makePrimitiveElement("output")}else{generics.push(makePrimitiveElement(null,{bindingName:makePrimitiveElement("output"),typeFilter:null,}))}parserState.typeFilter=typeFilter}if(isStringElem){skipWhitespace(parserState)}if(start>=end&&generics.length===0){return}if(parserState.userQuery[parserState.pos]==="="){if(parserState.isInBinding){throw["Cannot write ","="," twice in a binding"]}if(!isInGenerics){throw["Type parameter ","="," must be within generics list"]}const name=parserState.userQuery.slice(start,end).trim();if(name==="!"){throw["Type parameter ","="," key cannot be ","!"," never type"]}if(name.includes("!")){throw["Type parameter ","="," key cannot be ","!"," macro"]}if(name.includes("::")){throw["Type parameter ","="," key cannot contain ","::"," path"]}if(name.includes(":")){throw["Type parameter ","="," key cannot contain ",":"," type"]}parserState.isInBinding={name,generics}}else{elems.push(createQueryElement(query,parserState,parserState.userQuery.slice(start,end),generics,isInGenerics,),)}}}function checkExtraTypeFilterCharacters(start,parserState){const query=parserState.userQuery.slice(start,parserState.pos).trim();const match=query.match(REGEX_INVALID_TYPE_FILTER);if(match){throw["Unexpected ",match[0]," in type filter (before ",":",")",]}}function createQueryElement(query,parserState,name,generics,isInGenerics){const path=name.trim();if(path.length===0&&generics.length===0){throw["Unexpected ",parserState.userQuery[parserState.pos]]}if(query.literalSearch&&parserState.totalElems-parserState.genericsElems>0){throw["Cannot have more than one element if you use quotes"]}const typeFilter=parserState.typeFilter;parserState.typeFilter=null;if(name.trim()==="!"){if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive never type ","!"," and ",typeFilter," both specified",]}if(generics.length!==0){throw["Never type ","!"," does not accept generic parameters",]}const bindingName=parserState.isInBinding;parserState.isInBinding=null;return makePrimitiveElement("never",{bindingName})}const quadcolon=/::\s*::/.exec(path);if(path.startsWith("::")){throw["Paths cannot start with ","::"]}else if(quadcolon!==null){throw["Unexpected ",quadcolon[0]]}const pathSegments=path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/).map(x=>x.toLowerCase());if(pathSegments.length===0||(pathSegments.length===1&&pathSegments[0]==="")){if(generics.length>0||prevIs(parserState,">")){throw["Found generics without a path"]}else{throw["Unexpected ",parserState.userQuery[parserState.pos]]}}for(const[i,pathSegment]of pathSegments.entries()){if(pathSegment==="!"){if(i!==0){throw["Never type ","!"," is not associated item"]}pathSegments[i]="never"}}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}const bindingName=parserState.isInBinding;parserState.isInBinding=null;const bindings=new Map();const pathLast=pathSegments[pathSegments.length-1];return{name:name.trim(),id:null,fullPath:pathSegments,pathWithoutLast:pathSegments.slice(0,pathSegments.length-1),pathLast,normalizedPathLast:pathLast.replace(/_/g,""),generics:generics.filter(gen=>{if(gen.bindingName!==null){if(gen.name!==null){gen.bindingName.generics.unshift(gen)}bindings.set(gen.bindingName.name.toLowerCase().replace(/_/g,""),gen.bindingName.generics,);return false}return true}),bindings,typeFilter,bindingName,}}function makePrimitiveElement(name,extra){return Object.assign({name:name,id:null,fullPath:[name],pathWithoutLast:[],pathLast:name,normalizedPathLast:name,generics:[],bindings:new Map(),typeFilter:"primitive",bindingName:null,},extra)}function getStringElem(query,parserState,isInGenerics){if(isInGenerics){throw["Unexpected ","\""," in generics"]}else if(query.literalSearch){throw["Cannot have more than one literal search element"]}else if(parserState.totalElems-parserState.genericsElems>0){throw["Cannot use literal search when there is more than one element"]}parserState.pos+=1;const start=parserState.pos;const end=getIdentEndPosition(parserState);if(parserState.pos>=parserState.length){throw["Unclosed ","\""]}else if(parserState.userQuery[end]!=="\""){throw["Unexpected ",parserState.userQuery[end]," in a string element"]}else if(start===end){throw["Cannot have empty string element"]}parserState.pos+=1;query.literalSearch=true}function getIdentEndPosition(parserState){let afterIdent=consumeIdent(parserState);let end=parserState.pos;let macroExclamation=-1;while(parserState.pos0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]," (not a valid identifier)"]}else{throw["Unexpected ",c," (not a valid identifier)"]}parserState.pos+=1;afterIdent=consumeIdent(parserState);end=parserState.pos}if(macroExclamation!==-1){if(parserState.typeFilter===null){parserState.typeFilter="macro"}else if(parserState.typeFilter!=="macro"){throw["Invalid search type: macro ","!"," and ",parserState.typeFilter," both specified",]}end=macroExclamation}return end}function isSpecialStartCharacter(c){return"<\"".indexOf(c)!==-1}function isPathStart(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="::"}function consumeIdent(parserState){REGEX_IDENT.lastIndex=parserState.pos;const match=parserState.userQuery.match(REGEX_IDENT);if(match){parserState.pos+=match[0].length;return true}return false}function isPathSeparator(c){return c===":"||c===" "}class VlqHexDecoder{constructor(string,cons){this.string=string;this.cons=cons;this.offset=0;this.backrefQueue=[]}decodeList(){let c=this.string.charCodeAt(this.offset);const ret=[];while(c!==125){ret.push(this.decode());c=this.string.charCodeAt(this.offset)}this.offset+=1;return ret}decode(){let n=0;let c=this.string.charCodeAt(this.offset);if(c===123){this.offset+=1;return this.decodeList()}while(c<96){n=(n<<4)|(c&0xF);this.offset+=1;c=this.string.charCodeAt(this.offset)}n=(n<<4)|(c&0xF);const[sign,value]=[n&1,n>>1];this.offset+=1;return sign?-value:value}next(){const c=this.string.charCodeAt(this.offset);if(c>=48&&c<64){this.offset+=1;return this.backrefQueue[c-48]}if(c===96){this.offset+=1;return this.cons(0)}const result=this.cons(this.decode());this.backrefQueue.unshift(result);if(this.backrefQueue.length>16){this.backrefQueue.pop()}return result}}class RoaringBitmap{constructor(str){const strdecoded=atob(str);const u8array=new Uint8Array(strdecoded.length);for(let j=0;j=4){offsets=[];for(let j=0;j>3]&(1<<(j&0x7))){const runcount=(u8array[i]|(u8array[i+1]<<8));i+=2;this.containers.push(new RoaringBitmapRun(runcount,u8array.slice(i,i+(runcount*4)),));i+=runcount*4}else if(this.cardinalities[j]>=4096){this.containers.push(new RoaringBitmapBits(u8array.slice(i,i+8192)));i+=8192}else{const end=this.cardinalities[j]*2;this.containers.push(new RoaringBitmapArray(this.cardinalities[j],u8array.slice(i,i+end),));i+=end}}}contains(keyvalue){const key=keyvalue>>16;const value=keyvalue&0xFFFF;let left=0;let right=this.keys.length-1;while(left<=right){const mid=Math.floor((left+right)/2);const x=this.keys[mid];if(xkey){right=mid-1}else{return this.containers[mid].contains(value)}}return false}}class RoaringBitmapRun{constructor(runcount,array){this.runcount=runcount;this.array=array}contains(value){let left=0;let right=this.runcount-1;while(left<=right){const mid=Math.floor((left+right)/2);const i=mid*4;const start=this.array[i]|(this.array[i+1]<<8);const lenm1=this.array[i+2]|(this.array[i+3]<<8);if((start+lenm1)value){right=mid-1}else{return true}}return false}}class RoaringBitmapArray{constructor(cardinality,array){this.cardinality=cardinality;this.array=array}contains(value){let left=0;let right=this.cardinality-1;while(left<=right){const mid=Math.floor((left+right)/2);const i=mid*2;const x=this.array[i]|(this.array[i+1]<<8);if(xvalue){right=mid-1}else{return true}}return false}}class RoaringBitmapBits{constructor(array){this.array=array}contains(value){return!!(this.array[value>>3]&(1<<(value&7)))}}class NameTrie{constructor(){this.children=[];this.matches=[]}insert(name,id,tailTable){this.insertSubstring(name,0,id,tailTable)}insertSubstring(name,substart,id,tailTable){const l=name.length;if(substart===l){this.matches.push(id)}else{const sb=name.charCodeAt(substart);let child;if(this.children[sb]!==undefined){child=this.children[sb]}else{child=new NameTrie();this.children[sb]=child;let sste;if(substart>=2){const tail=name.substring(substart-2,substart+1);if(tailTable.has(tail)){sste=tailTable.get(tail)}else{sste=[];tailTable.set(tail,sste)}sste.push(child)}}child.insertSubstring(name,substart+1,id,tailTable)}}search(name,tailTable){const results=new Set();this.searchSubstringPrefix(name,0,results);if(results.size=3){const levParams=name.length>=6?new Lev2TParametricDescription(name.length):new Lev1TParametricDescription(name.length);this.searchLev(name,0,levParams,results);const tail=name.substring(0,3);if(tailTable.has(tail)){for(const entry of tailTable.get(tail)){entry.searchSubstringPrefix(name,3,results)}}}return[...results]}searchSubstringPrefix(name,substart,results){const l=name.length;if(substart===l){for(const match of this.matches){results.add(match)}let unprocessedChildren=[];for(const child of this.children){if(child){unprocessedChildren.push(child)}}let nextSet=[];while(unprocessedChildren.length!==0){const next=unprocessedChildren.pop();for(const child of next.children){if(child){nextSet.push(child)}}for(const match of next.matches){results.add(match)}if(unprocessedChildren.length===0){const tmp=unprocessedChildren;unprocessedChildren=nextSet;nextSet=tmp}}}else{const sb=name.charCodeAt(substart);if(this.children[sb]!==undefined){this.children[sb].searchSubstringPrefix(name,substart+1,results)}}}searchLev(name,substart,levParams,results){const stack=[[this,0]];const n=levParams.n;while(stack.length!==0){const[trie,levState]=stack.pop();for(const[charCode,child]of trie.children.entries()){if(!child){continue}const levPos=levParams.getPosition(levState);const vector=levParams.getVector(name,charCode,levPos,Math.min(name.length,levPos+(2*n)+1),);const newLevState=levParams.transition(levState,levPos,vector,);if(newLevState>=0){stack.push([child,newLevState]);if(levParams.isAccept(newLevState)){for(const match of child.matches){results.add(match)}}}}}}}class DocSearch{constructor(rawSearchIndex,rootPath,searchState){this.searchIndexDeprecated=new Map();this.searchIndexEmptyDesc=new Map();this.functionTypeFingerprint=new Uint32Array(0);this.typeNameIdMap=new Map();this.assocTypeIdNameMap=new Map();this.ALIASES=new Map();this.rootPath=rootPath;this.searchState=searchState;this.typeNameIdOfArray=this.buildTypeMapIndex("array");this.typeNameIdOfSlice=this.buildTypeMapIndex("slice");this.typeNameIdOfArrayOrSlice=this.buildTypeMapIndex("[]");this.typeNameIdOfTuple=this.buildTypeMapIndex("tuple");this.typeNameIdOfUnit=this.buildTypeMapIndex("unit");this.typeNameIdOfTupleOrUnit=this.buildTypeMapIndex("()");this.typeNameIdOfFn=this.buildTypeMapIndex("fn");this.typeNameIdOfFnMut=this.buildTypeMapIndex("fnmut");this.typeNameIdOfFnOnce=this.buildTypeMapIndex("fnonce");this.typeNameIdOfHof=this.buildTypeMapIndex("->");this.typeNameIdOfOutput=this.buildTypeMapIndex("output",true);this.typeNameIdOfReference=this.buildTypeMapIndex("reference");this.EMPTY_BINDINGS_MAP=new Map();this.EMPTY_GENERICS_ARRAY=[];this.TYPES_POOL=new Map();this.nameTrie=new NameTrie();this.tailTable=new Map();this.searchIndex=this.buildIndex(rawSearchIndex)}buildTypeMapIndex(name,isAssocType){if(name===""||name===null){return null}if(this.typeNameIdMap.has(name)){const obj=this.typeNameIdMap.get(name);obj.assocOnly=!!(isAssocType&&obj.assocOnly);return obj.id}else{const id=this.typeNameIdMap.size;this.typeNameIdMap.set(name,{id,assocOnly:!!isAssocType});return id}}buildItemSearchTypeAll(types,paths,lowercasePaths){return types&&types.length>0?types.map(type=>this.buildItemSearchType(type,paths,lowercasePaths)):this.EMPTY_GENERICS_ARRAY}buildItemSearchType(type,paths,lowercasePaths,isAssocType){const PATH_INDEX_DATA=0;const GENERICS_DATA=1;const BINDINGS_DATA=2;let pathIndex,generics,bindings;if(typeof type==="number"){pathIndex=type;generics=this.EMPTY_GENERICS_ARRAY;bindings=this.EMPTY_BINDINGS_MAP}else{pathIndex=type[PATH_INDEX_DATA];generics=this.buildItemSearchTypeAll(type[GENERICS_DATA],paths,lowercasePaths,);if(type.length>BINDINGS_DATA&&type[BINDINGS_DATA].length>0){bindings=new Map(type[BINDINGS_DATA].map(binding=>{const[assocType,constraints]=binding;return[this.buildItemSearchType(assocType,paths,lowercasePaths,true).id,this.buildItemSearchTypeAll(constraints,paths,lowercasePaths),]}))}else{bindings=this.EMPTY_BINDINGS_MAP}}let result;if(pathIndex<0){result={id:pathIndex,name:"",ty:TY_GENERIC,path:null,exactPath:null,generics,bindings,unboxFlag:true,}}else if(pathIndex===0){result={id:null,name:"",ty:null,path:null,exactPath:null,generics,bindings,unboxFlag:true,}}else{const item=lowercasePaths[pathIndex-1];const id=this.buildTypeMapIndex(item.name,isAssocType);if(isAssocType&&id!==null){this.assocTypeIdNameMap.set(id,paths[pathIndex-1].name)}result={id,name:paths[pathIndex-1].name,ty:item.ty,path:item.path,exactPath:item.exactPath,generics,bindings,unboxFlag:item.unboxFlag,}}const cr=this.TYPES_POOL.get(result.id);if(cr){if(cr.generics.length===result.generics.length&&cr.generics!==result.generics&&cr.generics.every((x,i)=>result.generics[i]===x)){result.generics=cr.generics}if(cr.bindings.size===result.bindings.size&&cr.bindings!==result.bindings){let ok=true;for(const[k,v]of cr.bindings.entries()){const v2=result.bindings.get(v);if(!v2){ok=false;break}if(v!==v2&&v.length===v2.length&&v.every((x,i)=>v2[i]===x)){result.bindings.set(k,v)}else if(v!==v2){ok=false;break}}if(ok){result.bindings=cr.bindings}}if(cr.ty===result.ty&&cr.path===result.path&&cr.bindings===result.bindings&&cr.generics===result.generics&&cr.ty===result.ty&&cr.name===result.name&&cr.unboxFlag===result.unboxFlag){return cr}}this.TYPES_POOL.set(result.id,result);return result}buildFunctionTypeFingerprint(type,output){let input=type.id;if(input===this.typeNameIdOfArray||input===this.typeNameIdOfSlice){input=this.typeNameIdOfArrayOrSlice}if(input===this.typeNameIdOfTuple||input===this.typeNameIdOfUnit){input=this.typeNameIdOfTupleOrUnit}if(input===this.typeNameIdOfFn||input===this.typeNameIdOfFnMut||input===this.typeNameIdOfFnOnce){input=this.typeNameIdOfHof}const hashint1=k=>{k=(~~k+0x7ed55d16)+(k<<12);k=(k ^ 0xc761c23c)^(k>>>19);k=(~~k+0x165667b1)+(k<<5);k=(~~k+0xd3a2646c)^(k<<9);k=(~~k+0xfd7046c5)+(k<<3);return(k ^ 0xb55a4f09)^(k>>>16)};const hashint2=k=>{k=~k+(k<<15);k ^=k>>>12;k+=k<<2;k ^=k>>>4;k=Math.imul(k,2057);return k ^(k>>16)};if(input!==null){const h0a=hashint1(input);const h0b=hashint2(input);const h1a=~~(h0a+Math.imul(h0b,2));const h1b=~~(h0a+Math.imul(h0b,3));const h2a=~~(h0a+Math.imul(h0b,4));const h2b=~~(h0a+Math.imul(h0b,5));output[0]|=(1<<(h0a%32))|(1<<(h1b%32));output[1]|=(1<<(h1a%32))|(1<<(h2b%32));output[2]|=(1<<(h2a%32))|(1<<(h0b%32));output[3]+=1}for(const g of type.generics){this.buildFunctionTypeFingerprint(g,output)}const fb={id:null,ty:0,generics:this.EMPTY_GENERICS_ARRAY,bindings:this.EMPTY_BINDINGS_MAP,};for(const[k,v]of type.bindings.entries()){fb.id=k;fb.generics=v;this.buildFunctionTypeFingerprint(fb,output)}}buildIndex(rawSearchIndex){const buildFunctionSearchTypeCallback=(paths,lowercasePaths)=>{const cb=functionSearchType=>{if(functionSearchType===0){return null}const INPUTS_DATA=0;const OUTPUT_DATA=1;let inputs;let output;if(typeof functionSearchType[INPUTS_DATA]==="number"){inputs=[this.buildItemSearchType(functionSearchType[INPUTS_DATA],paths,lowercasePaths,),]}else{inputs=this.buildItemSearchTypeAll(functionSearchType[INPUTS_DATA],paths,lowercasePaths,)}if(functionSearchType.length>1){if(typeof functionSearchType[OUTPUT_DATA]==="number"){output=[this.buildItemSearchType(functionSearchType[OUTPUT_DATA],paths,lowercasePaths,),]}else{output=this.buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA],paths,lowercasePaths,)}}else{output=[]}const where_clause=[];const l=functionSearchType.length;for(let i=2;i{const n=noop;return n});let descShard={crate,shard:0,start:0,len:itemDescShardDecoder.next(),promise:null,resolve:null,};const descShardList=[descShard];this.searchIndexDeprecated.set(crate,new RoaringBitmap(crateCorpus.c));this.searchIndexEmptyDesc.set(crate,new RoaringBitmap(crateCorpus.e));let descIndex=0;let lastParamNames=[];let normalizedName=crate.indexOf("_")===-1?crate:crate.replace(/_/g,"");const crateRow={crate,ty:3,name:crate,path:"",descShard,descIndex,exactPath:"",desc:crateCorpus.doc,parent:undefined,type:null,paramNames:lastParamNames,id,word:crate,normalizedName,bitIndex:0,implDisambiguator:null,};this.nameTrie.insert(normalizedName,id,this.tailTable);id+=1;searchIndex.push(crateRow);currentIndex+=1;if(!this.searchIndexEmptyDesc.get(crate).contains(0)){descIndex+=1}const itemTypes=crateCorpus.t;const itemNames=crateCorpus.n;const itemPaths=new Map(crateCorpus.q);const itemReexports=new Map(crateCorpus.r);const itemParentIdxDecoder=new VlqHexDecoder(crateCorpus.i,noop=>noop);const implDisambiguator=new Map(crateCorpus.b);const rawPaths=crateCorpus.p;const aliases=crateCorpus.a;const itemParamNames=new Map(crateCorpus.P);const lowercasePaths=[];const paths=[];const itemFunctionDecoder=new VlqHexDecoder(crateCorpus.f,buildFunctionSearchTypeCallback(paths,lowercasePaths),);let len=rawPaths.length;let lastPath=itemPaths.get(0);for(let i=0;i2&&elem[2]!==null){path=itemPaths.has(elem[2])?itemPaths.get(elem[2]):lastPath;lastPath=path}let exactPath=elem.length>3&&elem[3]!==null?itemPaths.get(elem[3]):path;const unboxFlag=elem.length>4&&!!elem[4];if(path===undefined){path=null}if(exactPath===undefined){exactPath=null}lowercasePaths.push({ty,name:name.toLowerCase(),path,exactPath,unboxFlag});paths[i]={ty,name,path,exactPath,unboxFlag}}lastPath="";len=itemTypes.length;let lastName="";let lastWord="";for(let i=0;i=descShard.len&&!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)){descShard={crate,shard:descShard.shard+1,start:descShard.start+descShard.len,len:itemDescShardDecoder.next(),promise:null,resolve:null,};descIndex=0;descShardList.push(descShard)}const name=itemNames[i]===""?lastName:itemNames[i];const word=itemNames[i]===""?lastWord:itemNames[i].toLowerCase();const path=itemPaths.has(i)?itemPaths.get(i):lastPath;const paramNames=itemParamNames.has(i)?itemParamNames.get(i).split(","):lastParamNames;const type=itemFunctionDecoder.next();if(type!==null){if(type){const fp=this.functionTypeFingerprint.subarray(id*4,(id+1)*4);for(const t of type.inputs){this.buildFunctionTypeFingerprint(t,fp)}for(const t of type.output){this.buildFunctionTypeFingerprint(t,fp)}for(const w of type.where_clause){for(const t of w){this.buildFunctionTypeFingerprint(t,fp)}}}}const itemParentIdx=itemParentIdxDecoder.next();normalizedName=word.indexOf("_")===-1?word:word.replace(/_/g,"");const row={crate,ty:itemTypes.charCodeAt(i)-65,name,path,descShard,descIndex,exactPath:itemReexports.has(i)?itemPaths.get(itemReexports.get(i)):path,parent:itemParentIdx>0?paths[itemParentIdx-1]:undefined,type,paramNames,id,word,normalizedName,bitIndex,implDisambiguator:implDisambiguator.has(i)?implDisambiguator.get(i):null,};this.nameTrie.insert(normalizedName,id,this.tailTable);id+=1;searchIndex.push(row);lastPath=row.path;lastParamNames=row.paramNames;if(!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)){descIndex+=1}lastName=name;lastWord=word}if(aliases){const currentCrateAliases=new Map();this.ALIASES.set(crate,currentCrateAliases);for(const alias_name in aliases){if(!Object.prototype.hasOwnProperty.call(aliases,alias_name)){continue}let currentNameAliases;if(currentCrateAliases.has(alias_name)){currentNameAliases=currentCrateAliases.get(alias_name)}else{currentNameAliases=[];currentCrateAliases.set(alias_name,currentNameAliases)}for(const local_alias of aliases[alias_name]){currentNameAliases.push(local_alias+currentIndex)}}}currentIndex+=itemTypes.length;this.searchState.descShards.set(crate,descShardList)}this.TYPES_POOL=new Map();return searchIndex}static parseQuery(userQuery){function itemTypeFromName(typename){const index=itemTypes.findIndex(i=>i===typename);if(index<0){throw["Unknown type filter ",typename]}return index}function convertTypeFilterOnElem(elem){if(elem.typeFilter!==null){let typeFilter=elem.typeFilter;if(typeFilter==="const"){typeFilter="constant"}elem.typeFilter=itemTypeFromName(typeFilter)}else{elem.typeFilter=NO_TYPE_FILTER}for(const elem2 of elem.generics){convertTypeFilterOnElem(elem2)}for(const constraints of elem.bindings.values()){for(const constraint of constraints){convertTypeFilterOnElem(constraint)}}}function newParsedQuery(userQuery){return{userQuery,elems:[],returned:[],foundElems:0,totalElems:0,literalSearch:false,hasReturnArrow:false,error:null,correction:null,proposeCorrectionFrom:null,proposeCorrectionTo:null,typeFingerprint:new Uint32Array(4),}}function parseInput(query,parserState){let foundStopChar=true;while(parserState.pos"){if(isReturnArrow(parserState)){query.hasReturnArrow=true;break}throw["Unexpected ",c," (did you mean ","->","?)"]}else if(parserState.pos>0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]]}throw["Unexpected ",c]}else if(c===" "){skipWhitespace(parserState);continue}if(!foundStopChar){let extra="";if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(parserState.typeFilter!==null){throw["Expected ",","," or ","->",...extra,", found ",c,]}throw["Expected ",",",", ",":"," or ","->",...extra,", found ",c,]}const before=query.elems.length;getFilteredNextElem(query,parserState,query.elems,false);if(query.elems.length===before){parserState.pos+=1}foundStopChar=false}if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}while(parserState.pos1}query.foundElems=query.elems.length+query.returned.length;query.totalElems=parserState.totalElems;return query}async execQuery(origParsedQuery,filterCrates,currentCrate){const results_others=new Map(),results_in_args=new Map(),results_returned=new Map();const parsedQuery=origParsedQuery;const queryLen=parsedQuery.elems.reduce((acc,next)=>acc+next.pathLast.length,0)+parsedQuery.returned.reduce((acc,next)=>acc+next.pathLast.length,0);const maxEditDistance=Math.floor(queryLen/3);const genericSymbols=new Map();const convertNameToId=(elem,isAssocType)=>{const loweredName=elem.pathLast.toLowerCase();if(this.typeNameIdMap.has(loweredName)&&(isAssocType||!this.typeNameIdMap.get(loweredName).assocOnly)){elem.id=this.typeNameIdMap.get(loweredName).id}else if(!parsedQuery.literalSearch){let match=null;let matchDist=maxEditDistance+1;let matchName="";for(const[name,{id,assocOnly}]of this.typeNameIdMap){const dist=Math.min(editDistance(name,loweredName,maxEditDistance),editDistance(name,elem.normalizedPathLast,maxEditDistance),);if(dist<=matchDist&&dist<=maxEditDistance&&(isAssocType||!assocOnly)){if(dist===matchDist&&matchName>name){continue}match=id;matchDist=dist;matchName=name}}if(match!==null){parsedQuery.correction=matchName}elem.id=match}if((elem.id===null&&parsedQuery.totalElems>1&&elem.typeFilter===-1&&elem.generics.length===0&&elem.bindings.size===0)||elem.typeFilter===TY_GENERIC){if(genericSymbols.has(elem.normalizedPathLast)){elem.id=genericSymbols.get(elem.normalizedPathLast)}else{elem.id=-(genericSymbols.size+1);genericSymbols.set(elem.normalizedPathLast,elem.id)}if(elem.typeFilter===-1&&elem.normalizedPathLast.length>=3){const maxPartDistance=Math.floor(elem.normalizedPathLast.length/3);let matchDist=maxPartDistance+1;let matchName="";for(const name of this.typeNameIdMap.keys()){const dist=editDistance(name,elem.normalizedPathLast,maxPartDistance,);if(dist<=matchDist&&dist<=maxPartDistance){if(dist===matchDist&&matchName>name){continue}matchDist=dist;matchName=name}}if(matchName!==""){parsedQuery.proposeCorrectionFrom=elem.name;parsedQuery.proposeCorrectionTo=matchName}}elem.typeFilter=TY_GENERIC}if(elem.generics.length>0&&elem.typeFilter===TY_GENERIC){parsedQuery.error=["Generic type parameter ",elem.name," does not accept generic parameters",]}for(const elem2 of elem.generics){convertNameToId(elem2)}elem.bindings=new Map(Array.from(elem.bindings.entries()).map(entry=>{const[name,constraints]=entry;if(!this.typeNameIdMap.has(name)){parsedQuery.error=["Type parameter ",name," does not exist",];return[0,[]]}for(const elem2 of constraints){convertNameToId(elem2,false)}return[this.typeNameIdMap.get(name).id,constraints]}),)};for(const elem of parsedQuery.elems){convertNameToId(elem,false);this.buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint)}for(const elem of parsedQuery.returned){convertNameToId(elem,false);this.buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint)}function createQueryResults(results_in_args,results_returned,results_others,parsedQuery){return{"in_args":results_in_args,"returned":results_returned,"others":results_others,"query":parsedQuery,}}const buildHrefAndPath=item=>{let displayPath;let href;const type=itemTypes[item.ty];const name=item.name;let path=item.path;let exactPath=item.exactPath;if(type==="mod"){displayPath=path+"::";href=this.rootPath+path.replace(/::/g,"/")+"/"+name+"/index.html"}else if(type==="import"){displayPath=item.path+"::";href=this.rootPath+item.path.replace(/::/g,"/")+"/index.html#reexport."+name}else if(type==="primitive"||type==="keyword"){displayPath="";exactPath="";href=this.rootPath+path.replace(/::/g,"/")+"/"+type+"."+name+".html"}else if(type==="externcrate"){displayPath="";href=this.rootPath+name+"/index.html"}else if(item.parent!==undefined){const myparent=item.parent;let anchor=type+"."+name;const parentType=itemTypes[myparent.ty];let pageType=parentType;let pageName=myparent.name;exactPath=`${myparent.exactPath}::${myparent.name}`;if(parentType==="primitive"){displayPath=myparent.name+"::";exactPath=myparent.name}else if(type==="structfield"&&parentType==="variant"){const enumNameIdx=item.path.lastIndexOf("::");const enumName=item.path.substr(enumNameIdx+2);path=item.path.substr(0,enumNameIdx);displayPath=path+"::"+enumName+"::"+myparent.name+"::";anchor="variant."+myparent.name+".field."+name;pageType="enum";pageName=enumName}else{displayPath=path+"::"+myparent.name+"::"}if(item.implDisambiguator!==null){anchor=item.implDisambiguator+"/"+anchor}href=this.rootPath+path.replace(/::/g,"/")+"/"+pageType+"."+pageName+".html#"+anchor}else{displayPath=item.path+"::";href=this.rootPath+item.path.replace(/::/g,"/")+"/"+type+"."+name+".html"}return[displayPath,href,`${exactPath}::${name}`]};function pathSplitter(path){const tmp=""+path.replace(/::/g,"::");if(tmp.endsWith("")){return tmp.slice(0,tmp.length-6)}return tmp}const transformResults=(results,typeInfo)=>{const duplicates=new Set();const out=[];for(const result of results){if(result.id!==-1){const res=buildHrefAndPath(this.searchIndex[result.id]);const obj=Object.assign({dist:result.dist,displayPath:pathSplitter(res[0]),},this.searchIndex[result.id]);obj.fullPath=res[2]+"|"+obj.ty;if(duplicates.has(obj.fullPath)){continue}if(obj.ty===TY_IMPORT&&duplicates.has(res[2])){continue}if(duplicates.has(res[2]+"|"+TY_IMPORT)){continue}duplicates.add(obj.fullPath);duplicates.add(res[2]);if(typeInfo!==null){obj.displayTypeSignature=this.formatDisplayTypeSignature(obj,typeInfo)}obj.href=res[1];out.push(obj);if(out.length>=MAX_RESULTS){break}}}return out};this.formatDisplayTypeSignature=async(obj,typeInfo)=>{const objType=obj.type;if(!objType){return{type:[],mappedNames:new Map(),whereClause:new Map()}}let fnInputs=null;let fnOutput=null;let mgens=null;if(typeInfo!=="elems"&&typeInfo!=="returned"){fnInputs=unifyFunctionTypes(objType.inputs,parsedQuery.elems,objType.where_clause,null,mgensScratch=>{fnOutput=unifyFunctionTypes(objType.output,parsedQuery.returned,objType.where_clause,mgensScratch,mgensOut=>{mgens=mgensOut;return true},0,);return!!fnOutput},0,)}else{const arr=typeInfo==="elems"?objType.inputs:objType.output;const highlighted=unifyFunctionTypes(arr,parsedQuery.elems,objType.where_clause,null,mgensOut=>{mgens=mgensOut;return true},0,);if(typeInfo==="elems"){fnInputs=highlighted}else{fnOutput=highlighted}}if(!fnInputs){fnInputs=objType.inputs}if(!fnOutput){fnOutput=objType.output}const mappedNames=new Map();const whereClause=new Map();const fnParamNames=obj.paramNames||[];const queryParamNames=[];const remapQuery=queryElem=>{if(queryElem.id!==null&&queryElem.id<0){queryParamNames[-1-queryElem.id]=queryElem.name}if(queryElem.generics.length>0){queryElem.generics.forEach(remapQuery)}if(queryElem.bindings.size>0){[...queryElem.bindings.values()].flat().forEach(remapQuery)}};parsedQuery.elems.forEach(remapQuery);parsedQuery.returned.forEach(remapQuery);const pushText=(fnType,result)=>{if(!!(result.length%2)===!!fnType.highlighted){result.push("")}else if(result.length===0&&!!fnType.highlighted){result.push("");result.push("")}result[result.length-1]+=fnType.name};const writeHof=(fnType,result)=>{const hofOutput=fnType.bindings.get(this.typeNameIdOfOutput)||[];const hofInputs=fnType.generics;pushText(fnType,result);pushText({name:" (",highlighted:false},result);let needsComma=false;for(const fnType of hofInputs){if(needsComma){pushText({name:", ",highlighted:false},result)}needsComma=true;writeFn(fnType,result)}pushText({name:hofOutput.length===0?")":") -> ",highlighted:false,},result);if(hofOutput.length>1){pushText({name:"(",highlighted:false},result)}needsComma=false;for(const fnType of hofOutput){if(needsComma){pushText({name:", ",highlighted:false},result)}needsComma=true;writeFn(fnType,result)}if(hofOutput.length>1){pushText({name:")",highlighted:false},result)}};const writeSpecialPrimitive=(fnType,result)=>{if(fnType.id===this.typeNameIdOfArray||fnType.id===this.typeNameIdOfSlice||fnType.id===this.typeNameIdOfTuple||fnType.id===this.typeNameIdOfUnit){const[ob,sb]=fnType.id===this.typeNameIdOfArray||fnType.id===this.typeNameIdOfSlice?["[","]"]:["(",")"];pushText({name:ob,highlighted:fnType.highlighted},result);onEachBtwn(fnType.generics,nested=>writeFn(nested,result),()=>pushText({name:", ",highlighted:false},result),);pushText({name:sb,highlighted:fnType.highlighted},result);return true}else if(fnType.id===this.typeNameIdOfReference){pushText({name:"&",highlighted:fnType.highlighted},result);let prevHighlighted=false;onEachBtwn(fnType.generics,value=>{prevHighlighted=!!value.highlighted;writeFn(value,result)},value=>pushText({name:" ",highlighted:prevHighlighted&&value.highlighted,},result),);return true}else if(fnType.id===this.typeNameIdOfFn){writeHof(fnType,result);return true}return false};const writeFn=(fnType,result)=>{if(fnType.id!==null&&fnType.id<0){if(fnParamNames[-1-fnType.id]===""){const generics=fnType.generics.length>0?fnType.generics:objType.where_clause[-1-fnType.id];for(const nested of generics){writeFn(nested,result)}return}else if(mgens){for(const[queryId,fnId]of mgens){if(fnId===fnType.id){mappedNames.set(queryParamNames[-1-queryId],fnParamNames[-1-fnType.id],)}}}pushText({name:fnParamNames[-1-fnType.id],highlighted:!!fnType.highlighted,},result);const where=[];onEachBtwn(fnType.generics,nested=>writeFn(nested,where),()=>pushText({name:" + ",highlighted:false},where),);if(where.length>0){whereClause.set(fnParamNames[-1-fnType.id],where)}}else{if(fnType.ty===TY_PRIMITIVE){if(writeSpecialPrimitive(fnType,result)){return}}else if(fnType.ty===TY_TRAIT&&(fnType.id===this.typeNameIdOfFn||fnType.id===this.typeNameIdOfFnMut||fnType.id===this.typeNameIdOfFnOnce)){writeHof(fnType,result);return}pushText(fnType,result);let hasBindings=false;if(fnType.bindings.size>0){onEachBtwn(fnType.bindings,([key,values])=>{const name=this.assocTypeIdNameMap.get(key);if(values.length===1&&values[0].id<0&&`${fnType.name}::${name}`===fnParamNames[-1-values[0].id]){for(const value of values){writeFn(value,[])}return true}if(!hasBindings){hasBindings=true;pushText({name:"<",highlighted:false},result)}pushText({name,highlighted:false},result);pushText({name:values.length!==1?"=(":"=",highlighted:false,},result);onEachBtwn(values||[],value=>writeFn(value,result),()=>pushText({name:" + ",highlighted:false},result),);if(values.length!==1){pushText({name:")",highlighted:false},result)}},()=>pushText({name:", ",highlighted:false},result),)}if(fnType.generics.length>0){pushText({name:hasBindings?", ":"<",highlighted:false},result)}onEachBtwn(fnType.generics,value=>writeFn(value,result),()=>pushText({name:", ",highlighted:false},result),);if(hasBindings||fnType.generics.length>0){pushText({name:">",highlighted:false},result)}}};const type=[];onEachBtwn(fnInputs,fnType=>writeFn(fnType,type),()=>pushText({name:", ",highlighted:false},type),);pushText({name:" -> ",highlighted:false},type);onEachBtwn(fnOutput,fnType=>writeFn(fnType,type),()=>pushText({name:", ",highlighted:false},type),);return{type,mappedNames,whereClause}};const sortResults=async(results,typeInfo,preferredCrate)=>{const userQuery=parsedQuery.userQuery;const normalizedUserQuery=parsedQuery.userQuery.toLowerCase();const isMixedCase=normalizedUserQuery!==userQuery;const result_list=[];const isReturnTypeQuery=parsedQuery.elems.length===0||typeInfo==="returned";for(const result of results.values()){result.item=this.searchIndex[result.id];result.word=this.searchIndex[result.id].word;if(isReturnTypeQuery){const resultItemType=result.item&&result.item.type;if(!resultItemType){continue}const inputs=resultItemType.inputs;const where_clause=resultItemType.where_clause;if(containsTypeFromQuery(inputs,where_clause)){result.path_dist*=100;result.dist*=100}}result_list.push(result)}result_list.sort((aaa,bbb)=>{let a;let b;if(isMixedCase){a=Number(aaa.item.name!==userQuery);b=Number(bbb.item.name!==userQuery);if(a!==b){return a-b}}a=Number(aaa.word!==normalizedUserQuery);b=Number(bbb.word!==normalizedUserQuery);if(a!==b){return a-b}a=Number(aaa.index<0);b=Number(bbb.index<0);if(a!==b){return a-b}if(parsedQuery.hasReturnArrow){a=Number(!isFnLikeTy(aaa.item.ty));b=Number(!isFnLikeTy(bbb.item.ty));if(a!==b){return a-b}}a=Number(aaa.path_dist);b=Number(bbb.path_dist);if(a!==b){return a-b}a=Number(aaa.index);b=Number(bbb.index);if(a!==b){return a-b}a=Number(aaa.dist);b=Number(bbb.dist);if(a!==b){return a-b}a=Number(this.searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex),);b=Number(this.searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex),);if(a!==b){return a-b}a=Number(aaa.item.crate!==preferredCrate);b=Number(bbb.item.crate!==preferredCrate);if(a!==b){return a-b}a=Number(aaa.word.length);b=Number(bbb.word.length);if(a!==b){return a-b}let aw=aaa.word;let bw=bbb.word;if(aw!==bw){return(aw>bw?+1:-1)}a=Number(this.searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex),);b=Number(this.searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex),);if(a!==b){return a-b}a=Number(aaa.item.ty);b=Number(bbb.item.ty);if(a!==b){return a-b}aw=aaa.item.path;bw=bbb.item.path;if(aw!==bw){return(aw>bw?+1:-1)}return 0});return transformResults(result_list,typeInfo)};function unifyFunctionTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb,unboxingDepth,){if(unboxingDepth>=UNBOXING_LIMIT){return null}const mgens=mgensIn===null?null:new Map(mgensIn);if(queryElems.length===0){return solutionCb(mgens)?fnTypesIn:null}if(!fnTypesIn||fnTypesIn.length===0){return null}const ql=queryElems.length;const fl=fnTypesIn.length;if(ql===1&&queryElems[0].generics.length===0&&queryElems[0].bindings.size===0){const queryElem=queryElems[0];for(const[i,fnType]of fnTypesIn.entries()){if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens)){continue}if(fnType.id!==null&&fnType.id<0&&queryElem.id!==null&&queryElem.id<0){if(mgens&&mgens.has(queryElem.id)&&mgens.get(queryElem.id)!==fnType.id){continue}const mgensScratch=new Map(mgens);mgensScratch.set(queryElem.id,fnType.id);if(!solutionCb||solutionCb(mgensScratch)){const highlighted=[...fnTypesIn];highlighted[i]=Object.assign({highlighted:true,},fnType,{generics:whereClause[-1-fnType.id],});return highlighted}}else if(solutionCb(mgens?new Map(mgens):null)){const highlighted=[...fnTypesIn];highlighted[i]=Object.assign({highlighted:true,},fnType,{generics:unifyGenericTypes(fnType.generics,queryElem.generics,whereClause,mgens?new Map(mgens):null,solutionCb,unboxingDepth,)||fnType.generics,});return highlighted}}for(const[i,fnType]of fnTypesIn.entries()){if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1,)){continue}if(fnType.id<0){const highlightedGenerics=unifyFunctionTypes(whereClause[(-fnType.id)-1],queryElems,whereClause,mgens,solutionCb,unboxingDepth+1,);if(highlightedGenerics){const highlighted=[...fnTypesIn];highlighted[i]=Object.assign({highlighted:true,},fnType,{generics:highlightedGenerics,});return highlighted}}else{const highlightedGenerics=unifyFunctionTypes([...Array.from(fnType.bindings.values()).flat(),...fnType.generics],queryElems,whereClause,mgens?new Map(mgens):null,solutionCb,unboxingDepth+1,);if(highlightedGenerics){const highlighted=[...fnTypesIn];highlighted[i]=Object.assign({},fnType,{generics:highlightedGenerics,bindings:new Map([...fnType.bindings.entries()].map(([k,v])=>{return[k,highlightedGenerics.splice(0,v.length)]})),});return highlighted}}}return false}const fnTypes=fnTypesIn.slice();const flast=fl-1;const qlast=ql-1;const queryElem=queryElems[qlast];let queryElemsTmp=null;for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens)){continue}let mgensScratch;if(fnType.id!==null&&queryElem.id!==null&&fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(queryElem.id)&&mgensScratch.get(queryElem.id)!==fnType.id){continue}mgensScratch.set(queryElem.id,fnType.id)}else{mgensScratch=mgens}fnTypes[i]=fnTypes[flast];fnTypes.length=flast;if(!queryElemsTmp){queryElemsTmp=queryElems.slice(0,qlast)}let unifiedGenerics=[];let unifiedGenericsMgens=null;const passesUnification=unifyFunctionTypes(fnTypes,queryElemsTmp,whereClause,mgensScratch,mgensScratch=>{if(fnType.generics.length===0&&queryElem.generics.length===0&&fnType.bindings.size===0&&queryElem.bindings.size===0){return solutionCb(mgensScratch)}const solution=unifyFunctionTypeCheckBindings(fnType,queryElem,whereClause,mgensScratch,unboxingDepth,);if(!solution){return false}const simplifiedGenerics=solution.simplifiedGenerics;for(const simplifiedMgens of solution.mgens){unifiedGenerics=unifyGenericTypes(simplifiedGenerics,queryElem.generics,whereClause,simplifiedMgens,solutionCb,unboxingDepth,);if(unifiedGenerics!==null){unifiedGenericsMgens=simplifiedMgens;return true}}return false},unboxingDepth,);if(passesUnification){passesUnification.length=fl;passesUnification[flast]=passesUnification[i];passesUnification[i]=Object.assign({},fnType,{highlighted:true,generics:unifiedGenerics,bindings:new Map([...fnType.bindings.entries()].map(([k,v])=>{return[k,queryElem.bindings.has(k)?unifyFunctionTypes(v,queryElem.bindings.get(k),whereClause,unifiedGenericsMgens,solutionCb,unboxingDepth,):unifiedGenerics.splice(0,v.length)]})),});return passesUnification}fnTypes[flast]=fnTypes[i];fnTypes[i]=fnType;fnTypes.length=fl}for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1,)){continue}const generics=fnType.id!==null&&fnType.id<0?whereClause[(-fnType.id)-1]:fnType.generics;const bindings=fnType.bindings?Array.from(fnType.bindings.values()).flat():[];const passesUnification=unifyFunctionTypes(fnTypes.toSpliced(i,1,...bindings,...generics),queryElems,whereClause,mgens,solutionCb,unboxingDepth+1,);if(passesUnification){const highlightedGenerics=passesUnification.slice(i,i+generics.length+bindings.length,);const highlightedFnType=Object.assign({},fnType,{generics:highlightedGenerics,bindings:new Map([...fnType.bindings.entries()].map(([k,v])=>{return[k,highlightedGenerics.splice(0,v.length)]})),});return passesUnification.toSpliced(i,generics.length+bindings.length,highlightedFnType,)}}return null}function unifyGenericTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb,unboxingDepth,){if(unboxingDepth>=UNBOXING_LIMIT){return null}const mgens=mgensIn===null?null:new Map(mgensIn);if(queryElems.length===0){return solutionCb(mgens)?fnTypesIn:null}if(!fnTypesIn||fnTypesIn.length===0){return null}const fnType=fnTypesIn[0];const queryElem=queryElems[0];if(unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens)){if(fnType.id!==null&&fnType.id<0&&queryElem.id!==null&&queryElem.id<0){if(!mgens||!mgens.has(queryElem.id)||mgens.get(queryElem.id)===fnType.id){const mgensScratch=new Map(mgens);mgensScratch.set(queryElem.id,fnType.id);const fnTypesRemaining=unifyGenericTypes(fnTypesIn.slice(1),queryElems.slice(1),whereClause,mgensScratch,solutionCb,unboxingDepth,);if(fnTypesRemaining){const highlighted=[fnType,...fnTypesRemaining];highlighted[0]=Object.assign({highlighted:true,},fnType,{generics:whereClause[-1-fnType.id],});return highlighted}}}else{let unifiedGenerics;const fnTypesRemaining=unifyGenericTypes(fnTypesIn.slice(1),queryElems.slice(1),whereClause,mgens,mgensScratch=>{const solution=unifyFunctionTypeCheckBindings(fnType,queryElem,whereClause,mgensScratch,unboxingDepth,);if(!solution){return false}const simplifiedGenerics=solution.simplifiedGenerics;for(const simplifiedMgens of solution.mgens){unifiedGenerics=unifyGenericTypes(simplifiedGenerics,queryElem.generics,whereClause,simplifiedMgens,solutionCb,unboxingDepth,);if(unifiedGenerics!==null){return true}}},unboxingDepth,);if(fnTypesRemaining){const highlighted=[fnType,...fnTypesRemaining];highlighted[0]=Object.assign({highlighted:true,},fnType,{generics:unifiedGenerics||fnType.generics,});return highlighted}}}if(unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1,)){let highlightedRemaining;if(fnType.id!==null&&fnType.id<0){const highlightedGenerics=unifyFunctionTypes(whereClause[(-fnType.id)-1],[queryElem],whereClause,mgens,mgensScratch=>{const hl=unifyGenericTypes(fnTypesIn.slice(1),queryElems.slice(1),whereClause,mgensScratch,solutionCb,unboxingDepth,);if(hl){highlightedRemaining=hl}return hl},unboxingDepth+1,);if(highlightedGenerics){return[Object.assign({highlighted:true,},fnType,{generics:highlightedGenerics,}),...highlightedRemaining]}}else{const highlightedGenerics=unifyGenericTypes([...Array.from(fnType.bindings.values()).flat(),...fnType.generics,],[queryElem],whereClause,mgens,mgensScratch=>{const hl=unifyGenericTypes(fnTypesIn.slice(1),queryElems.slice(1),whereClause,mgensScratch,solutionCb,unboxingDepth,);if(hl){highlightedRemaining=hl}return hl},unboxingDepth+1,);if(highlightedGenerics){return[Object.assign({},fnType,{generics:highlightedGenerics,bindings:new Map([...fnType.bindings.entries()].map(([k,v])=>{return[k,highlightedGenerics.splice(0,v.length)]})),}),...highlightedRemaining]}}}return null}const unifyFunctionTypeIsMatchCandidate=(fnType,queryElem,mgensIn)=>{if(!typePassesFilter(queryElem.typeFilter,fnType.ty)){return false}if(fnType.id!==null&&fnType.id<0&&queryElem.id!==null&&queryElem.id<0){if(mgensIn&&mgensIn.has(queryElem.id)&&mgensIn.get(queryElem.id)!==fnType.id){return false}return true}else{if(queryElem.id===this.typeNameIdOfArrayOrSlice&&(fnType.id===this.typeNameIdOfSlice||fnType.id===this.typeNameIdOfArray)){}else if(queryElem.id===this.typeNameIdOfTupleOrUnit&&(fnType.id===this.typeNameIdOfTuple||fnType.id===this.typeNameIdOfUnit)){}else if(queryElem.id===this.typeNameIdOfHof&&(fnType.id===this.typeNameIdOfFn||fnType.id===this.typeNameIdOfFnMut||fnType.id===this.typeNameIdOfFnOnce)){}else if(fnType.id!==queryElem.id||queryElem.id===null){return false}if((fnType.generics.length+fnType.bindings.size)===0&&queryElem.generics.length!==0){return false}if(fnType.bindings.size0){const fnTypePath=fnType.path!==undefined&&fnType.path!==null?fnType.path.split("::"):[];if(queryElemPathLength>fnTypePath.length){return false}let i=0;for(const path of fnTypePath){if(path===queryElem.pathWithoutLast[i]){i+=1;if(i>=queryElemPathLength){break}}}if(i0){let mgensSolutionSet=[mgensIn];for(const[name,constraints]of queryElem.bindings.entries()){if(mgensSolutionSet.length===0){return false}if(!fnType.bindings.has(name)){return false}const fnTypeBindings=fnType.bindings.get(name);mgensSolutionSet=mgensSolutionSet.flatMap(mgens=>{const newSolutions=[];unifyFunctionTypes(fnTypeBindings,constraints,whereClause,mgens,newMgens=>{newSolutions.push(newMgens);return false},unboxingDepth,);return newSolutions})}if(mgensSolutionSet.length===0){return false}const binds=Array.from(fnType.bindings.entries()).flatMap(entry=>{const[name,constraints]=entry;if(queryElem.bindings.has(name)){return[]}else{return constraints}});if(simplifiedGenerics.length>0){simplifiedGenerics=[...binds,...simplifiedGenerics]}else{simplifiedGenerics=binds}return{simplifiedGenerics,mgens:mgensSolutionSet}}return{simplifiedGenerics,mgens:[mgensIn]}}function unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth,){if(unboxingDepth>=UNBOXING_LIMIT){return false}if(fnType.id!==null&&fnType.id<0){if(!whereClause){return false}return checkIfInList(whereClause[(-fnType.id)-1],queryElem,whereClause,mgens,unboxingDepth,)}else if(fnType.unboxFlag&&(fnType.generics.length>0||fnType.bindings.size>0)){const simplifiedGenerics=[...fnType.generics,...Array.from(fnType.bindings.values()).flat(),];return checkIfInList(simplifiedGenerics,queryElem,whereClause,mgens,unboxingDepth,)}return false}function containsTypeFromQuery(list,where_clause){if(!list)return false;for(const ty of parsedQuery.returned){if(ty.id!==null&&ty.id<0){continue}if(checkIfInList(list,ty,where_clause,null,0)){return true}}for(const ty of parsedQuery.elems){if(ty.id!==null&&ty.id<0){continue}if(checkIfInList(list,ty,where_clause,null,0)){return true}}return false}function checkIfInList(list,elem,whereClause,mgens,unboxingDepth){for(const entry of list){if(checkType(entry,elem,whereClause,mgens,unboxingDepth)){return true}}return false}const checkType=(row,elem,whereClause,mgens,unboxingDepth)=>{if(unboxingDepth>=UNBOXING_LIMIT){return false}if(row.id!==null&&elem.id!==null&&row.id>0&&elem.id>0&&elem.pathWithoutLast.length===0&&row.generics.length===0&&elem.generics.length===0&&row.bindings.size===0&&elem.bindings.size===0&&elem.id!==this.typeNameIdOfArrayOrSlice&&elem.id!==this.typeNameIdOfHof&&elem.id!==this.typeNameIdOfTupleOrUnit){return row.id===elem.id&&typePassesFilter(elem.typeFilter,row.ty)}else{return unifyFunctionTypes([row],[elem],whereClause,mgens,()=>true,unboxingDepth,)}};const checkTypeMgensForConflict=mgens=>{if(!mgens){return true}const fnTypes=new Set();for(const[_qid,fid]of mgens){if(fnTypes.has(fid)){return false}fnTypes.add(fid)}return true};function checkPath(contains,ty){if(contains.length===0){return 0}const maxPathEditDistance=Math.floor(contains.reduce((acc,next)=>acc+next.length,0)/3,);let ret_dist=maxPathEditDistance+1;const path=ty.path.split("::");if(ty.parent&&ty.parent.name){path.push(ty.parent.name.toLowerCase())}const length=path.length;const clength=contains.length;pathiter:for(let i=length-clength;i>=0;i-=1){let dist_total=0;for(let x=0;xmaxPathEditDistance){continue pathiter}dist_total+=dist}}ret_dist=Math.min(ret_dist,Math.round(dist_total/clength))}return ret_dist>maxPathEditDistance?null:ret_dist}function typePassesFilter(filter,type){if(filter<=NO_TYPE_FILTER||filter===type)return true;const name=itemTypes[type];switch(itemTypes[filter]){case"constant":return name==="associatedconstant";case"fn":return name==="method"||name==="tymethod";case"type":return name==="primitive"||name==="associatedtype";case"trait":return name==="traitalias"}return false}function createAliasFromItem(item){return{crate:item.crate,name:item.name,path:item.path,descShard:item.descShard,descIndex:item.descIndex,exactPath:item.exactPath,ty:item.ty,parent:item.parent,type:item.type,is_alias:true,bitIndex:item.bitIndex,implDisambiguator:item.implDisambiguator,}}const handleAliases=async(ret,query,filterCrates,currentCrate)=>{const lowerQuery=query.toLowerCase();const aliases=[];const crateAliases=[];if(filterCrates!==null){if(this.ALIASES.has(filterCrates)&&this.ALIASES.get(filterCrates).has(lowerQuery)){const query_aliases=this.ALIASES.get(filterCrates).get(lowerQuery);for(const alias of query_aliases){aliases.push(createAliasFromItem(this.searchIndex[alias]))}}}else{for(const[crate,crateAliasesIndex]of this.ALIASES){if(crateAliasesIndex.has(lowerQuery)){const pushTo=crate===currentCrate?crateAliases:aliases;const query_aliases=crateAliasesIndex.get(lowerQuery);for(const alias of query_aliases){pushTo.push(createAliasFromItem(this.searchIndex[alias]))}}}}const sortFunc=(aaa,bbb)=>{if(aaa.path{return this.searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex)?"":this.searchState.loadDesc(alias)};const[crateDescs,descs]=await Promise.all([Promise.all(crateAliases.map(fetchDesc)),Promise.all(aliases.map(fetchDesc)),]);const pushFunc=alias=>{alias.alias=query;const res=buildHrefAndPath(alias);alias.displayPath=pathSplitter(res[0]);alias.fullPath=alias.displayPath+alias.name;alias.href=res[1];ret.others.unshift(alias);if(ret.others.length>MAX_RESULTS){ret.others.pop()}};aliases.forEach((alias,i)=>{alias.desc=descs[i]});aliases.forEach(pushFunc);crateAliases.forEach((alias,i)=>{alias.desc=crateDescs[i]});crateAliases.forEach(pushFunc)};function addIntoResults(results,fullId,id,index,dist,path_dist,maxEditDistance){if(dist<=maxEditDistance||index!==-1){if(results.has(fullId)){const result=results.get(fullId);if(result.dontValidate||result.dist<=dist){return}}results.set(fullId,{id:id,index:index,dontValidate:parsedQuery.literalSearch,dist:dist,path_dist:path_dist,})}}function handleArgs(row,pos,results){if(!row||(filterCrates!==null&&row.crate!==filterCrates)){return}const rowType=row.type;if(!rowType){return}const tfpDist=compareTypeFingerprints(row.id,parsedQuery.typeFingerprint,);if(tfpDist===null){return}if(results.size>=MAX_RESULTS&&tfpDist>results.max_dist){return}if(!unifyFunctionTypes(rowType.inputs,parsedQuery.elems,rowType.where_clause,null,mgens=>{return unifyFunctionTypes(rowType.output,parsedQuery.returned,rowType.where_clause,mgens,checkTypeMgensForConflict,0,)},0,)){return}results.max_dist=Math.max(results.max_dist||0,tfpDist);addIntoResults(results,row.id.toString(),pos,0,tfpDist,0,Number.MAX_VALUE)}const compareTypeFingerprints=(fullId,queryFingerprint)=>{const fh0=this.functionTypeFingerprint[fullId*4];const fh1=this.functionTypeFingerprint[(fullId*4)+1];const fh2=this.functionTypeFingerprint[(fullId*4)+2];const[qh0,qh1,qh2]=queryFingerprint;const[in0,in1,in2]=[fh0&qh0,fh1&qh1,fh2&qh2];if((in0 ^ qh0)||(in1 ^ qh1)||(in2 ^ qh2)){return null}return this.functionTypeFingerprint[(fullId*4)+3]};const innerRunQuery=()=>{if(parsedQuery.foundElems===1&&!parsedQuery.hasReturnArrow){const elem=parsedQuery.elems[0];const handleNameSearch=id=>{const row=this.searchIndex[id];if(!typePassesFilter(elem.typeFilter,row.ty)||(filterCrates!==null&&row.crate!==filterCrates)){return}let pathDist=0;if(elem.fullPath.length>1){pathDist=checkPath(elem.pathWithoutLast,row);if(pathDist===null){return}}if(parsedQuery.literalSearch){if(row.word===elem.pathLast){addIntoResults(results_others,row.id,id,0,0,pathDist)}}else{addIntoResults(results_others,row.id,id,row.normalizedName.indexOf(elem.normalizedPathLast),editDistance(row.normalizedName,elem.normalizedPathLast,maxEditDistance,),pathDist,maxEditDistance,)}};if(elem.normalizedPathLast!==""){const last=elem.normalizedPathLast;for(const id of this.nameTrie.search(last,this.tailTable)){handleNameSearch(id)}}const length=this.searchIndex.length;for(let i=0,nSearchIndex=length;i0){const sortQ=(a,b)=>{const ag=a.generics.length===0&&a.bindings.size===0;const bg=b.generics.length===0&&b.bindings.size===0;if(ag!==bg){return ag-bg}const ai=a.id>0;const bi=b.id>0;return ai-bi};parsedQuery.elems.sort(sortQ);parsedQuery.returned.sort(sortQ);for(let i=0,nSearchIndex=this.searchIndex.length;i{const descs=await Promise.all(list.map(result=>{return this.searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex)?"":this.searchState.loadDesc(result)}));for(const[i,result]of list.entries()){result.desc=descs[i]}}));if(parsedQuery.error!==null&&ret.others.length!==0){ret.query.error=null}return ret}}let rawSearchIndex;let docSearch;const longItemTypes=["keyword","primitive type","module","extern crate","re-export","struct","enum","function","type alias","static","trait","","trait method","method","struct field","enum variant","macro","assoc type","constant","assoc const","union","foreign type","existential type","attribute macro","derive macro","trait alias",];let currentResults;function printTab(nb){let iter=0;let foundCurrentTab=false;let foundCurrentResultSet=false;onEachLazy(document.getElementById("search-tabs").childNodes,elem=>{if(nb===iter){addClass(elem,"selected");foundCurrentTab=true}else{removeClass(elem,"selected")}iter+=1});const isTypeSearch=(nb>0||iter===1);iter=0;onEachLazy(document.getElementById("results").childNodes,elem=>{if(nb===iter){addClass(elem,"active");foundCurrentResultSet=true}else{removeClass(elem,"active")}iter+=1});if(foundCurrentTab&&foundCurrentResultSet){searchState.currentTab=nb;const correctionsElem=document.getElementsByClassName("search-corrections");if(isTypeSearch){removeClass(correctionsElem[0],"hidden")}else{addClass(correctionsElem[0],"hidden")}}else if(nb!==0){printTab(0)}}function buildUrl(search,filterCrates){let extra="?search="+encodeURIComponent(search);if(filterCrates!==null){extra+="&filter-crate="+encodeURIComponent(filterCrates)}return getNakedUrl()+extra+window.location.hash}function getFilterCrates(){const elem=document.getElementById("crate-search");if(elem&&elem.value!=="all crates"&&window.searchIndex.has(elem.value)){return elem.value}return null}function nextTab(direction){const next=(searchState.currentTab+direction+3)%searchState.focusedByTab.length;searchState.focusedByTab[searchState.currentTab]=document.activeElement;printTab(next);focusSearchResult()}function focusSearchResult(){const target=searchState.focusedByTab[searchState.currentTab]||document.querySelectorAll(".search-results.active a").item(0)||document.querySelectorAll("#search-tabs button").item(searchState.currentTab);searchState.focusedByTab[searchState.currentTab]=null;if(target){target.focus()}}async function addTab(array,query,display){const extraClass=display?" active":"";const output=document.createElement(array.length===0&&query.error===null?"div":"ul",);if(array.length>0){output.className="search-results "+extraClass;const lis=Promise.all(array.map(async item=>{const name=item.name;const type=itemTypes[item.ty];const longType=longItemTypes[item.ty];const typeName=longType.length!==0?`${longType}`:"?";const link=document.createElement("a");link.className="result-"+type;link.href=item.href;const resultName=document.createElement("span");resultName.className="result-name";resultName.insertAdjacentHTML("beforeend",`${typeName}`);link.appendChild(resultName);let alias=" ";if(item.is_alias){alias=`
\ +${item.alias} - see \ +
`}resultName.insertAdjacentHTML("beforeend",`
${alias}\ +${item.displayPath}${name}\ +
`);const description=document.createElement("div");description.className="desc";description.insertAdjacentHTML("beforeend",item.desc);if(item.displayTypeSignature){const{type,mappedNames,whereClause}=await item.displayTypeSignature;const displayType=document.createElement("div");type.forEach((value,index)=>{if(index%2!==0){const highlight=document.createElement("strong");highlight.appendChild(document.createTextNode(value));displayType.appendChild(highlight)}else{displayType.appendChild(document.createTextNode(value))}});if(mappedNames.size>0||whereClause.size>0){let addWhereLineFn=()=>{const line=document.createElement("div");line.className="where";line.appendChild(document.createTextNode("where"));displayType.appendChild(line);addWhereLineFn=()=>{}};for(const[qname,name]of mappedNames){if(name===qname){continue}addWhereLineFn();const line=document.createElement("div");line.className="where";line.appendChild(document.createTextNode(` ${qname} matches `));const lineStrong=document.createElement("strong");lineStrong.appendChild(document.createTextNode(name));line.appendChild(lineStrong);displayType.appendChild(line)}for(const[name,innerType]of whereClause){if(innerType.length<=1){continue}addWhereLineFn();const line=document.createElement("div");line.className="where";line.appendChild(document.createTextNode(` ${name}: `));innerType.forEach((value,index)=>{if(index%2!==0){const highlight=document.createElement("strong");highlight.appendChild(document.createTextNode(value));line.appendChild(highlight)}else{line.appendChild(document.createTextNode(value))}});displayType.appendChild(line)}}displayType.className="type-signature";link.appendChild(displayType)}link.appendChild(description);return link}));lis.then(lis=>{for(const li of lis){output.appendChild(li)}})}else if(query.error===null){const dlroChannel=`https://doc.rust-lang.org/${getVar("channel")}`;output.className="search-failed"+extraClass;output.innerHTML="No results :(
"+"Try on DuckDuckGo?

"+"Or try looking in one of these:"}return output}function makeTabHeader(tabNb,text,nbElems){const fmtNbElems=nbElems<10?`\u{2007}(${nbElems})\u{2007}\u{2007}`:nbElems<100?`\u{2007}(${nbElems})\u{2007}`:`\u{2007}(${nbElems})`;if(searchState.currentTab===tabNb){return""}return""}async function showResults(results,go_to_first,filterCrates){const search=searchState.outputElement();if(go_to_first||(results.others.length===1&&getSettingValue("go-to-only-result")==="true")){window.onunload=()=>{};searchState.removeQueryParameters();const elem=document.createElement("a");elem.href=results.others[0].href;removeClass(elem,"active");document.body.appendChild(elem);elem.click();return}if(results.query===undefined){results.query=DocSearch.parseQuery(searchState.input.value)}currentResults=results.query.userQuery;let currentTab=searchState.currentTab;if((currentTab===0&&results.others.length===0)||(currentTab===1&&results.in_args.length===0)||(currentTab===2&&results.returned.length===0)){if(results.others.length!==0){currentTab=0}else if(results.in_args.length){currentTab=1}else if(results.returned.length){currentTab=2}}let crates="";if(rawSearchIndex.size>1){crates="
in 
"+"
"}let output=`
\ +

Results

${crates}
`;if(results.query.error!==null){const error=results.query.error;error.forEach((value,index)=>{value=value.split("<").join("<").split(">").join(">");if(index%2!==0){error[index]=`${value.replaceAll(" ", " ")}`}else{error[index]=value}});output+=`

Query parser error: "${error.join("")}".

`;output+="
"+makeTabHeader(0,"In Names",results.others.length)+"
";currentTab=0}else if(results.query.foundElems<=1&&results.query.returned.length===0){output+="
"+makeTabHeader(0,"In Names",results.others.length)+makeTabHeader(1,"In Parameters",results.in_args.length)+makeTabHeader(2,"In Return Types",results.returned.length)+"
"}else{const signatureTabTitle=results.query.elems.length===0?"In Function Return Types":results.query.returned.length===0?"In Function Parameters":"In Function Signatures";output+="
"+makeTabHeader(0,signatureTabTitle,results.others.length)+"
";currentTab=0}if(results.query.correction!==null){const orig=results.query.returned.length>0?results.query.returned[0].name:results.query.elems[0].name;output+="

"+`Type "${orig}" not found. `+"Showing results for closest type name "+`"${results.query.correction}" instead.

`}if(results.query.proposeCorrectionFrom!==null){const orig=results.query.proposeCorrectionFrom;const targ=results.query.proposeCorrectionTo;output+="

"+`Type "${orig}" not found and used as generic parameter. `+`Consider searching for "${targ}" instead.

`}const[ret_others,ret_in_args,ret_returned]=await Promise.all([addTab(results.others,results.query,currentTab===0),addTab(results.in_args,results.query,currentTab===1),addTab(results.returned,results.query,currentTab===2),]);const resultsElem=document.createElement("div");resultsElem.id="results";resultsElem.appendChild(ret_others);resultsElem.appendChild(ret_in_args);resultsElem.appendChild(ret_returned);search.innerHTML=output;if(searchState.rustdocToolbar){search.querySelector(".main-heading").appendChild(searchState.rustdocToolbar)}const crateSearch=document.getElementById("crate-search");if(crateSearch){crateSearch.addEventListener("input",updateCrate)}search.appendChild(resultsElem);searchState.showResults(search);const elems=document.getElementById("search-tabs").childNodes;searchState.focusedByTab=[];let i=0;for(const elem of elems){const j=i;elem.onclick=()=>printTab(j);searchState.focusedByTab.push(null);i+=1}printTab(currentTab)}function updateSearchHistory(url){if(!browserSupportsHistoryApi()){return}const params=searchState.getQueryStringParams();if(!history.state&&!params.search){history.pushState(null,"",url)}else{history.replaceState(null,"",url)}}async function search(forced){const query=DocSearch.parseQuery(searchState.input.value.trim());let filterCrates=getFilterCrates();if(!forced&&query.userQuery===currentResults){if(query.userQuery.length>0){putBackSearch()}return}searchState.setLoadingSearch();const params=searchState.getQueryStringParams();if(filterCrates===null&¶ms["filter-crate"]!==undefined){filterCrates=params["filter-crate"]}searchState.title="\""+query.userQuery+"\" Search - Rust";updateSearchHistory(buildUrl(query.userQuery,filterCrates));await showResults(await docSearch.execQuery(query,filterCrates,window.currentCrate),params.go_to_first,filterCrates)}function onSearchSubmit(e){e.preventDefault();searchState.clearInputTimeout();search()}function putBackSearch(){const search_input=searchState.input;if(!searchState.input){return}if(search_input.value!==""&&!searchState.isDisplayed()){searchState.showResults();if(browserSupportsHistoryApi()){history.replaceState(null,"",buildUrl(search_input.value,getFilterCrates()))}document.title=searchState.title}}function registerSearchEvents(){const params=searchState.getQueryStringParams();if(searchState.input.value===""){searchState.input.value=params.search||""}const searchAfter500ms=()=>{searchState.clearInputTimeout();if(searchState.input.value.length===0){searchState.hideResults()}else{searchState.timeout=setTimeout(search,500)}};searchState.input.onkeyup=searchAfter500ms;searchState.input.oninput=searchAfter500ms;document.getElementsByClassName("search-form")[0].onsubmit=onSearchSubmit;searchState.input.onchange=e=>{if(e.target!==document.activeElement){return}searchState.clearInputTimeout();setTimeout(search,0)};searchState.input.onpaste=searchState.input.onchange;searchState.outputElement().addEventListener("keydown",e=>{if(e.altKey||e.ctrlKey||e.shiftKey||e.metaKey){return}if(e.which===38){const previous=document.activeElement.previousElementSibling;if(previous){previous.focus()}else{searchState.focus()}e.preventDefault()}else if(e.which===40){const next=document.activeElement.nextElementSibling;if(next){next.focus()}const rect=document.activeElement.getBoundingClientRect();if(window.innerHeight-rect.bottom{if(e.which===40){focusSearchResult();e.preventDefault()}});searchState.input.addEventListener("focus",()=>{putBackSearch()});searchState.input.addEventListener("blur",()=>{searchState.input.placeholder=searchState.input.origPlaceholder});if(browserSupportsHistoryApi()){const previousTitle=document.title;window.addEventListener("popstate",e=>{const params=searchState.getQueryStringParams();document.title=previousTitle;currentResults=null;if(params.search&¶ms.search.length>0){searchState.input.value=params.search;e.preventDefault();search()}else{searchState.input.value="";searchState.hideResults()}})}window.onpageshow=()=>{const qSearch=searchState.getQueryStringParams().search;if(searchState.input.value===""&&qSearch){searchState.input.value=qSearch}search()}}function updateCrate(ev){if(ev.target.value==="all crates"){const query=searchState.input.value.trim();updateSearchHistory(buildUrl(query,null))}currentResults=null;search(true)}function initSearch(searchIndx){rawSearchIndex=searchIndx;if(typeof window!=="undefined"){docSearch=new DocSearch(rawSearchIndex,ROOT_PATH,searchState);registerSearchEvents();if(window.searchState.getQueryStringParams().search){search()}}else if(typeof exports!=="undefined"){docSearch=new DocSearch(rawSearchIndex,ROOT_PATH,searchState);exports.docSearch=docSearch;exports.parseQuery=DocSearch.parseQuery}}if(typeof exports!=="undefined"){exports.initSearch=initSearch}if(typeof window!=="undefined"){window.initSearch=initSearch;if(window.searchIndex!==undefined){initSearch(window.searchIndex)}}else{initSearch(new Map())}class ParametricDescription{constructor(w,n,minErrors){this.w=w;this.n=n;this.minErrors=minErrors}isAccept(absState){const state=Math.floor(absState/(this.w+1));const offset=absState%(this.w+1);return this.w-offset+this.minErrors[state]<=this.n}getPosition(absState){return absState%(this.w+1)}getVector(name,charCode,pos,end){let vector=0;for(let i=pos;i>5;const bitStart=bitLoc&31;if(bitStart+bitsPerValue<=32){return((data[dataLoc]>>bitStart)&this.MASKS[bitsPerValue-1])}else{const part=32-bitStart;return ~~(((data[dataLoc]>>bitStart)&this.MASKS[part-1])+((data[1+dataLoc]&this.MASKS[bitsPerValue-part-1])<{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true"}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked)}});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference"}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value}elem.addEventListener("change",ev=>{changeSetting(ev.target.name,ev.target.value)})})}function buildSettingsPageSections(settings){let output="";for(const setting of settings){if(setting==="hr"){output+="
";continue}const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\ +
+
${setting_name}
+
`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\ + `});output+=`\ +
+
`}else{const checked=setting["default"]===true?" checked":"";output+=`\ +
\ + \ +
`}}return output}function buildSettingsPage(){const theme_names=getVar("themes").split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Hide persistent navigation bar","js_name":"hide-sidebar","default":false,},{"name":"Hide table of contents","js_name":"hide-toc","default":false,},{"name":"Hide module navigation","js_name":"hide-modnav","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},{"name":"Use sans serif fonts","js_name":"sans-serif-fonts","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`
${buildSettingsPageSections(settings)}
`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover"}el.innerHTML=innerHTML;if(isSettingsPage){document.getElementById(MAIN_ID).appendChild(el)}else{el.setAttribute("tabindex","-1");getSettingsButton().appendChild(el)}return el}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display="";onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"),el=>{const val=getSettingValue(el.id);const checked=val==="true";if(checked!==el.checked&&val!==null){el.checked=checked}})}function settingsBlurHandler(event){if(!getHelpButton().contains(document.activeElement)&&!getHelpButton().contains(event.relatedTarget)&&!getSettingsButton().contains(document.activeElement)&&!getSettingsButton().contains(event.relatedTarget)){window.hidePopoverMenus()}}if(!isSettingsPage){const settingsButton=getSettingsButton();const settingsMenu=document.getElementById("settings");settingsButton.onclick=event=>{if(settingsMenu.contains(event.target)){return}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals();if(shouldDisplaySettings){displaySettings()}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler});settingsMenu.onblur=settingsBlurHandler}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings()}removeClass(getSettingsButton(),"rotate")},0)})() \ No newline at end of file diff --git a/crates/doc/static.files/src-script-8fee9dc5.js b/crates/doc/static.files/src-script-8fee9dc5.js new file mode 100644 index 000000000000..d0aebb85103b --- /dev/null +++ b/crates/doc/static.files/src-script-8fee9dc5.js @@ -0,0 +1 @@ +"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth{removeClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","false")};window.rustdocShowSourceSidebar=()=>{addClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","true")};window.rustdocToggleSrcSidebar=()=>{if(document.documentElement.classList.contains("src-sidebar-expanded")){window.rustdocCloseSourceSidebar()}else{window.rustdocShowSourceSidebar()}};function createSrcSidebar(){const container=document.querySelector("nav.sidebar");const sidebar=document.createElement("div");sidebar.id="src-sidebar";let hasFoundFile=false;for(const[key,source]of srcIndex){source[NAME_OFFSET]=key;hasFoundFile=createDirEntry(source,sidebar,"",hasFoundFile)}container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus()}}function highlightSrcLines(){const match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);if(!match){return}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to{onEachLazy(e.getElementsByTagName("a"),i_e=>{removeClass(i_e,"line-highlighted")})});for(let i=from;i<=to;++i){elem=document.getElementById(i);if(!elem){break}addClass(elem,"line-highlighted")}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,null,"#"+name);highlightSrcLines()}else{location.replace("#"+name)}window.scrollTo(x,y)};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp}set_fragment(prev_line_id+"-"+cur_line_id)}else{prev_line_id=cur_line_id;set_fragment(cur_line_id)}}}());window.addEventListener("hashchange",highlightSrcLines);onEachLazy(document.getElementsByClassName("src-line-numbers"),el=>{el.addEventListener("click",handleSrcHighlight)});highlightSrcLines();window.createSrcSidebar=createSrcSidebar})() \ No newline at end of file diff --git a/crates/doc/static.files/storage-3a5871a4.js b/crates/doc/static.files/storage-3a5871a4.js new file mode 100644 index 000000000000..0f15a182c88f --- /dev/null +++ b/crates/doc/static.files/storage-3a5871a4.js @@ -0,0 +1,23 @@ +"use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=(function(){const currentTheme=document.getElementById("themeStyle");return currentTheme instanceof HTMLLinkElement?currentTheme:null})();const settingsDataset=(function(){const settingsElement=document.getElementById("default-settings");return settingsElement&&settingsElement.dataset?settingsElement.dataset:null})();function getSettingValue(settingName){const current=getCurrentValue(settingName);if(current===null&&settingsDataset!==null){const def=settingsDataset[settingName.replace(/-/g,"_")];if(def!==undefined){return def}}return current}const localStoredTheme=getSettingValue("theme");function hasClass(elem,className){return!!elem&&!!elem.classList&&elem.classList.contains(className)}function addClass(elem,className){if(elem&&elem.classList){elem.classList.add(className)}}function removeClass(elem,className){if(elem&&elem.classList){elem.classList.remove(className)}}function onEach(arr,func){for(const elem of arr){if(func(elem)){return true}}return false}function onEachLazy(lazyArray,func){return onEach(Array.prototype.slice.call(lazyArray),func)}function updateLocalStorage(name,value){try{if(value===null){window.localStorage.removeItem("rustdoc-"+name)}else{window.localStorage.setItem("rustdoc-"+name,value)}}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name)}catch(e){return null}}function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.getAttribute("data-"+name):null}function switchTheme(newThemeName,saveTheme){const themeNames=(getVar("themes")||"").split(",").filter(t=>t);themeNames.push(...builtinThemes);if(newThemeName===null||themeNames.indexOf(newThemeName)===-1){return}if(saveTheme){updateLocalStorage("theme",newThemeName)}document.documentElement.setAttribute("data-theme",newThemeName);if(builtinThemes.indexOf(newThemeName)!==-1){if(window.currentTheme&&window.currentTheme.parentNode){window.currentTheme.parentNode.removeChild(window.currentTheme);window.currentTheme=null}}else{const newHref=getVar("root-path")+encodeURIComponent(newThemeName)+getVar("resource-suffix")+".css";if(!window.currentTheme){if(document.readyState==="loading"){document.write(``);window.currentTheme=(function(){const currentTheme=document.getElementById("themeStyle");return currentTheme instanceof HTMLLinkElement?currentTheme:null})()}else{window.currentTheme=document.createElement("link");window.currentTheme.rel="stylesheet";window.currentTheme.id="themeStyle";window.currentTheme.href=newHref;document.documentElement.appendChild(window.currentTheme)}}else if(newHref!==window.currentTheme.href){window.currentTheme.href=newHref}}}const updateTheme=(function(){const mql=window.matchMedia("(prefers-color-scheme: dark)");function updateTheme(){if(getSettingValue("use-system-theme")!=="false"){const lightTheme=getSettingValue("preferred-light-theme")||"light";const darkTheme=getSettingValue("preferred-dark-theme")||"dark";updateLocalStorage("use-system-theme","true");switchTheme(mql.matches?darkTheme:lightTheme,true)}else{switchTheme(getSettingValue("theme"),false)}}mql.addEventListener("change",updateTheme);return updateTheme})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&localStoredTheme!==null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("preferred-dark-theme",localStoredTheme)}}updateTheme();if(getSettingValue("source-sidebar-show")==="true"){addClass(document.documentElement,"src-sidebar-expanded")}if(getSettingValue("hide-sidebar")==="true"){addClass(document.documentElement,"hide-sidebar")}if(getSettingValue("hide-toc")==="true"){addClass(document.documentElement,"hide-toc")}if(getSettingValue("hide-modnav")==="true"){addClass(document.documentElement,"hide-modnav")}if(getSettingValue("sans-serif-fonts")==="true"){addClass(document.documentElement,"sans-serif")}function updateSidebarWidth(){const desktopSidebarWidth=getSettingValue("desktop-sidebar-width");if(desktopSidebarWidth&&desktopSidebarWidth!=="null"){document.documentElement.style.setProperty("--desktop-sidebar-width",desktopSidebarWidth+"px",)}const srcSidebarWidth=getSettingValue("src-sidebar-width");if(srcSidebarWidth&&srcSidebarWidth!=="null"){document.documentElement.style.setProperty("--src-sidebar-width",srcSidebarWidth+"px",)}}updateSidebarWidth();window.addEventListener("pageshow",ev=>{if(ev.persisted){setTimeout(updateTheme,0);setTimeout(updateSidebarWidth,0)}});class RustdocSearchElement extends HTMLElement{constructor(){super()}connectedCallback(){const rootPath=getVar("root-path");const currentCrate=getVar("current-crate");this.innerHTML=``}}window.customElements.define("rustdoc-search",RustdocSearchElement);class RustdocToolbarElement extends HTMLElement{constructor(){super()}connectedCallback(){if(this.firstElementChild){return}const rootPath=getVar("root-path");this.innerHTML=` +
+ Settings +
+
+ Help +
+ `}}window.customElements.define("rustdoc-toolbar",RustdocToolbarElement) \ No newline at end of file diff --git a/crates/doc/trait.impl/core/clone/trait.Clone.js b/crates/doc/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 000000000000..ccb7084606c3 --- /dev/null +++ b/crates/doc/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Clone for AllocationStatus"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[276]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/cmp/trait.Eq.js b/crates/doc/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 000000000000..d046d856175a --- /dev/null +++ b/crates/doc/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Eq for AllocationStatus"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[263]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/cmp/trait.PartialEq.js b/crates/doc/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 000000000000..307a8938c5c7 --- /dev/null +++ b/crates/doc/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl PartialEq for AllocationStatus"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[284]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/default/trait.Default.js b/crates/doc/trait.impl/core/default/trait.Default.js new file mode 100644 index 000000000000..bc949881d132 --- /dev/null +++ b/crates/doc/trait.impl/core/default/trait.Default.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Default for RoundRobin"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[291]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/fmt/trait.Debug.js b/crates/doc/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 000000000000..c5ab51acd5fc --- /dev/null +++ b/crates/doc/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Debug for AllocationStatus"],["impl<'a, T: Debug> Debug for ArbitraryPointer<'a, T>"],["impl<const BYTES: usize> Debug for PointerGenerator<BYTES>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1100]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/future/future/trait.Future.js b/crates/doc/trait.impl/core/future/future/trait.Future.js new file mode 100644 index 000000000000..753ed720e983 --- /dev/null +++ b/crates/doc/trait.impl/core/future/future/trait.Future.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Future for JoinHandle"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[301]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/marker/trait.Copy.js b/crates/doc/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 000000000000..f0f158b0a148 --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.Copy.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Copy for AllocationStatus"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[275]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/marker/trait.Freeze.js b/crates/doc/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 000000000000..6d431b102768 --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Freeze for AllocationStatus",1,["kani::arbitrary_ptr::AllocationStatus"]],["impl Freeze for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl Freeze for JoinHandle",1,["kani::futures::JoinHandle"]],["impl Freeze for RoundRobin",1,["kani::futures::RoundRobin"]],["impl<'a, T> Freeze for ArbitraryPointer<'a, T>",1,["kani::arbitrary_ptr::ArbitraryPointer"]],["impl<T> Freeze for ShadowMem<T>
where\n T: Freeze,
",1,["kani::shadow::ShadowMem"]],["impl<const BYTES: usize> Freeze for PointerGenerator<BYTES>",1,["kani::arbitrary_ptr::PointerGenerator"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2585]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/marker/trait.Send.js b/crates/doc/trait.impl/core/marker/trait.Send.js new file mode 100644 index 000000000000..b4d28b361cb7 --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Send for AllocationStatus",1,["kani::arbitrary_ptr::AllocationStatus"]],["impl Send for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl Send for JoinHandle",1,["kani::futures::JoinHandle"]],["impl Send for RoundRobin",1,["kani::futures::RoundRobin"]],["impl<'a, T> !Send for ArbitraryPointer<'a, T>",1,["kani::arbitrary_ptr::ArbitraryPointer"]],["impl<T> Send for ShadowMem<T>
where\n T: Send,
",1,["kani::shadow::ShadowMem"]],["impl<const BYTES: usize> Send for PointerGenerator<BYTES>",1,["kani::arbitrary_ptr::PointerGenerator"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2538]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/marker/trait.StructuralPartialEq.js b/crates/doc/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 000000000000..38e560f44448 --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl StructuralPartialEq for AllocationStatus"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[320]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/marker/trait.Sync.js b/crates/doc/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 000000000000..1932c9b3ad72 --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Sync for AllocationStatus",1,["kani::arbitrary_ptr::AllocationStatus"]],["impl Sync for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl Sync for JoinHandle",1,["kani::futures::JoinHandle"]],["impl Sync for RoundRobin",1,["kani::futures::RoundRobin"]],["impl<'a, T> !Sync for ArbitraryPointer<'a, T>",1,["kani::arbitrary_ptr::ArbitraryPointer"]],["impl<T> Sync for ShadowMem<T>
where\n T: Sync,
",1,["kani::shadow::ShadowMem"]],["impl<const BYTES: usize> Sync for PointerGenerator<BYTES>",1,["kani::arbitrary_ptr::PointerGenerator"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2538]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/marker/trait.Unpin.js b/crates/doc/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 000000000000..101c4bcae76e --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl Unpin for AllocationStatus",1,["kani::arbitrary_ptr::AllocationStatus"]],["impl Unpin for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl Unpin for JoinHandle",1,["kani::futures::JoinHandle"]],["impl Unpin for RoundRobin",1,["kani::futures::RoundRobin"]],["impl<'a, T> Unpin for ArbitraryPointer<'a, T>",1,["kani::arbitrary_ptr::ArbitraryPointer"]],["impl<T> Unpin for ShadowMem<T>
where\n T: Unpin,
",1,["kani::shadow::ShadowMem"]],["impl<const BYTES: usize> Unpin for PointerGenerator<BYTES>",1,["kani::arbitrary_ptr::PointerGenerator"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2561]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/crates/doc/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 000000000000..fb339510a6e2 --- /dev/null +++ b/crates/doc/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl RefUnwindSafe for AllocationStatus",1,["kani::arbitrary_ptr::AllocationStatus"]],["impl RefUnwindSafe for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl RefUnwindSafe for JoinHandle",1,["kani::futures::JoinHandle"]],["impl RefUnwindSafe for RoundRobin",1,["kani::futures::RoundRobin"]],["impl<'a, T> RefUnwindSafe for ArbitraryPointer<'a, T>
where\n T: RefUnwindSafe,
",1,["kani::arbitrary_ptr::ArbitraryPointer"]],["impl<T> RefUnwindSafe for ShadowMem<T>
where\n T: RefUnwindSafe,
",1,["kani::shadow::ShadowMem"]],["impl<const BYTES: usize> RefUnwindSafe for PointerGenerator<BYTES>",1,["kani::arbitrary_ptr::PointerGenerator"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3162]} \ No newline at end of file diff --git a/crates/doc/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/crates/doc/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 000000000000..296bd8466eff --- /dev/null +++ b/crates/doc/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[["impl UnwindSafe for AllocationStatus",1,["kani::arbitrary_ptr::AllocationStatus"]],["impl UnwindSafe for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl UnwindSafe for JoinHandle",1,["kani::futures::JoinHandle"]],["impl UnwindSafe for RoundRobin",1,["kani::futures::RoundRobin"]],["impl<'a, T> UnwindSafe for ArbitraryPointer<'a, T>
where\n T: RefUnwindSafe,
",1,["kani::arbitrary_ptr::ArbitraryPointer"]],["impl<T> UnwindSafe for ShadowMem<T>
where\n T: UnwindSafe,
",1,["kani::shadow::ShadowMem"]],["impl<const BYTES: usize> UnwindSafe for PointerGenerator<BYTES>",1,["kani::arbitrary_ptr::PointerGenerator"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3090]} \ No newline at end of file diff --git a/crates/doc/trait.impl/kani/futures/trait.SchedulingStrategy.js b/crates/doc/trait.impl/kani/futures/trait.SchedulingStrategy.js new file mode 100644 index 000000000000..d9cb395c1279 --- /dev/null +++ b/crates/doc/trait.impl/kani/futures/trait.SchedulingStrategy.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/crates/doc/trait.impl/kani/invariant/trait.Invariant.js b/crates/doc/trait.impl/kani/invariant/trait.Invariant.js new file mode 100644 index 000000000000..d9cb395c1279 --- /dev/null +++ b/crates/doc/trait.impl/kani/invariant/trait.Invariant.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/crates/doc/trait.impl/kani/trait.Arbitrary.js b/crates/doc/trait.impl/kani/trait.Arbitrary.js new file mode 100644 index 000000000000..d9cb395c1279 --- /dev/null +++ b/crates/doc/trait.impl/kani/trait.Arbitrary.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["kani",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[11]} \ No newline at end of file diff --git a/crates/index.html b/crates/index.html new file mode 100644 index 000000000000..3a397e6adec0 --- /dev/null +++ b/crates/index.html @@ -0,0 +1,192 @@ + + + + + + Crates Documentation - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Crates documentation

+

Kani currently ships with a kani crate that provide APIs to allow users to +write and configure their harnesses. +These APIs are tightly coupled with each Kani version, so they are not +published yet at https://crates.io.

+

You can find their latest documentation here:

+
    +
  • kani: This crate +provide APIs to write Kani harnesses.
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/css/chrome.css b/css/chrome.css new file mode 100644 index 000000000000..21c08b930f04 --- /dev/null +++ b/css/chrome.css @@ -0,0 +1,495 @@ +/* CSS for UI elements (a.k.a. chrome) */ + +@import 'variables.css'; + +::-webkit-scrollbar { + background: var(--bg); +} +::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} +html { + scrollbar-color: var(--scrollbar) var(--bg); +} +#searchresults a, +.content a:link, +a:visited, +a > .hljs { + color: var(--links); +} + +/* Menu Bar */ + +#menu-bar, +#menu-bar-hover-placeholder { + z-index: 101; + margin: auto calc(0px - var(--page-padding)); +} +#menu-bar { + position: relative; + display: flex; + flex-wrap: wrap; + background-color: var(--bg); + border-bottom-color: var(--bg); + border-bottom-width: 1px; + border-bottom-style: solid; +} +#menu-bar.sticky, +.js #menu-bar-hover-placeholder:hover + #menu-bar, +.js #menu-bar:hover, +.js.sidebar-visible #menu-bar { + position: -webkit-sticky; + position: sticky; + top: 0 !important; +} +#menu-bar-hover-placeholder { + position: sticky; + position: -webkit-sticky; + top: 0; + height: var(--menu-bar-height); +} +#menu-bar.bordered { + border-bottom-color: var(--table-border-color); +} +#menu-bar i, #menu-bar .icon-button { + position: relative; + padding: 0 8px; + z-index: 10; + line-height: var(--menu-bar-height); + cursor: pointer; + transition: color 0.5s; +} +@media only screen and (max-width: 420px) { + #menu-bar i, #menu-bar .icon-button { + padding: 0 5px; + } +} + +.icon-button { + border: none; + background: none; + padding: 0; + color: inherit; +} +.icon-button i { + margin: 0; +} + +.right-buttons { + margin: 0 15px; +} +.right-buttons a { + text-decoration: none; +} + +.left-buttons { + display: flex; + margin: 0 5px; +} +.no-js .left-buttons { + display: none; +} + +.menu-title { + display: inline-block; + font-weight: 200; + font-size: 2.4rem; + line-height: var(--menu-bar-height); + text-align: center; + margin: 0; + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.js .menu-title { + cursor: pointer; +} + +.menu-bar, +.menu-bar:visited, +.nav-chapters, +.nav-chapters:visited, +.mobile-nav-chapters, +.mobile-nav-chapters:visited, +.menu-bar .icon-button, +.menu-bar a i { + color: var(--icons); +} + +.menu-bar i:hover, +.menu-bar .icon-button:hover, +.nav-chapters:hover, +.mobile-nav-chapters i:hover { + color: var(--icons-hover); +} + +/* Nav Icons */ + +.nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + + position: fixed; + top: 0; + bottom: 0; + margin: 0; + max-width: 150px; + min-width: 90px; + + display: flex; + justify-content: center; + align-content: center; + flex-direction: column; + + transition: color 0.5s, background-color 0.5s; +} + +.nav-chapters:hover { + text-decoration: none; + background-color: var(--theme-hover); + transition: background-color 0.15s, color 0.15s; +} + +.nav-wrapper { + margin-top: 50px; + display: none; +} + +.mobile-nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + width: 90px; + border-radius: 5px; + background-color: var(--sidebar-bg); +} + +.previous { + float: left; +} + +.next { + float: right; + right: var(--page-padding); +} + +@media only screen and (max-width: 1080px) { + .nav-wide-wrapper { display: none; } + .nav-wrapper { display: block; } +} + +@media only screen and (max-width: 1380px) { + .sidebar-visible .nav-wide-wrapper { display: none; } + .sidebar-visible .nav-wrapper { display: block; } +} + +/* Inline code */ + +:not(pre) > .hljs { + display: inline; + padding: 0.1em 0.3em; + border-radius: 3px; +} + +:not(pre):not(a) > .hljs { + color: var(--inline-code-color); + overflow-x: initial; +} + +a:hover > .hljs { + text-decoration: underline; +} + +pre { + position: relative; +} +pre > .buttons { + position: absolute; + z-index: 100; + right: 5px; + top: 5px; + + color: var(--sidebar-fg); + cursor: pointer; +} +pre > .buttons :hover { + color: var(--sidebar-active); +} +pre > .buttons i { + margin-left: 8px; +} +pre > .buttons button { + color: inherit; + background: transparent; + border: none; + cursor: inherit; +} +pre > .result { + margin-top: 10px; +} + +/* Search */ + +#searchresults a { + text-decoration: none; +} + +mark { + border-radius: 2px; + padding: 0 3px 1px 3px; + margin: 0 -3px -1px -3px; + background-color: var(--search-mark-bg); + transition: background-color 300ms linear; + cursor: pointer; +} + +mark.fade-out { + background-color: rgba(0,0,0,0) !important; + cursor: auto; +} + +.searchbar-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} + +#searchbar { + width: 100%; + margin: 5px auto 0px auto; + padding: 10px 16px; + transition: box-shadow 300ms ease-in-out; + border: 1px solid var(--searchbar-border-color); + border-radius: 3px; + background-color: var(--searchbar-bg); + color: var(--searchbar-fg); +} +#searchbar:focus, +#searchbar.active { + box-shadow: 0 0 3px var(--searchbar-shadow-color); +} + +.searchresults-header { + font-weight: bold; + font-size: 1em; + padding: 18px 0 0 5px; + color: var(--searchresults-header-fg); +} + +.searchresults-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); + border-bottom: 1px dashed var(--searchresults-border-color); +} + +ul#searchresults { + list-style: none; + padding-left: 20px; +} +ul#searchresults li { + margin: 10px 0px; + padding: 2px; + border-radius: 2px; +} +ul#searchresults li.focus { + background-color: var(--searchresults-li-bg); +} +ul#searchresults span.teaser { + display: block; + clear: both; + margin: 5px 0 0 20px; + font-size: 0.8em; +} +ul#searchresults span.teaser em { + font-weight: bold; + font-style: normal; +} + +/* Sidebar */ + +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: var(--sidebar-width); + font-size: 0.875em; + box-sizing: border-box; + -webkit-overflow-scrolling: touch; + overscroll-behavior-y: contain; + background-color: var(--sidebar-bg); + color: var(--sidebar-fg); +} +.sidebar-resizing { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +.js:not(.sidebar-resizing) .sidebar { + transition: transform 0.3s; /* Animation: slide away */ +} +.sidebar code { + line-height: 2em; +} +.sidebar .sidebar-scrollbox { + overflow-y: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + padding: 10px 10px; +} +.sidebar .sidebar-resize-handle { + position: absolute; + cursor: col-resize; + width: 0; + right: 0; + top: 0; + bottom: 0; +} +.js .sidebar .sidebar-resize-handle { + cursor: col-resize; + width: 5px; +} +.sidebar-hidden .sidebar { + transform: translateX(calc(0px - var(--sidebar-width))); +} +.sidebar::-webkit-scrollbar { + background: var(--sidebar-bg); +} +.sidebar::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} + +.sidebar-visible .page-wrapper { + transform: translateX(var(--sidebar-width)); +} +@media only screen and (min-width: 620px) { + .sidebar-visible .page-wrapper { + transform: none; + margin-left: var(--sidebar-width); + } +} + +.chapter { + list-style: none outside none; + padding-left: 0; + line-height: 2.2em; +} + +.chapter ol { + width: 100%; +} + +.chapter li { + display: flex; + color: var(--sidebar-non-existant); +} +.chapter li a { + display: block; + padding: 0; + text-decoration: none; + color: var(--sidebar-fg); +} + +.chapter li a:hover { + color: var(--sidebar-active); +} + +.chapter li a.active { + color: var(--sidebar-active); +} + +.chapter li > a.toggle { + cursor: pointer; + display: block; + margin-left: auto; + padding: 0 10px; + user-select: none; + opacity: 0.68; +} + +.chapter li > a.toggle div { + transition: transform 0.5s; +} + +/* collapse the section */ +.chapter li:not(.expanded) + li > ol { + display: none; +} + +.chapter li.chapter-item { + line-height: 1.5em; + margin-top: 0.6em; +} + +.chapter li.expanded > a.toggle div { + transform: rotate(90deg); +} + +.spacer { + width: 100%; + height: 3px; + margin: 5px 0px; +} +.chapter .spacer { + background-color: var(--sidebar-spacer); +} + +@media (-moz-touch-enabled: 1), (pointer: coarse) { + .chapter li a { padding: 5px 0; } + .spacer { margin: 10px 0; } +} + +.section { + list-style: none outside none; + padding-left: 20px; + line-height: 1.9em; +} + +/* Theme Menu Popup */ + +.theme-popup { + position: absolute; + left: 10px; + top: var(--menu-bar-height); + z-index: 1000; + border-radius: 4px; + font-size: 0.7em; + color: var(--fg); + background: var(--theme-popup-bg); + border: 1px solid var(--theme-popup-border); + margin: 0; + padding: 0; + list-style: none; + display: none; +} +.theme-popup .default { + color: var(--icons); +} +.theme-popup .theme { + width: 100%; + border: 0; + margin: 0; + padding: 2px 10px; + line-height: 25px; + white-space: nowrap; + text-align: left; + cursor: pointer; + color: inherit; + background: inherit; + font-size: inherit; +} +.theme-popup .theme:hover { + background-color: var(--theme-hover); +} +.theme-popup .theme:hover:first-child, +.theme-popup .theme:hover:last-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; +} diff --git a/css/general.css b/css/general.css new file mode 100644 index 000000000000..ef2ba5048917 --- /dev/null +++ b/css/general.css @@ -0,0 +1,182 @@ +/* Base styles and content styles */ + +@import 'variables.css'; + +:root { + /* Browser default font-size is 16px, this way 1 rem = 10px */ + font-size: 62.5%; +} + +html { + font-family: "Open Sans", sans-serif; + color: var(--fg); + background-color: var(--bg); + text-size-adjust: none; + -webkit-text-size-adjust: none; +} + +body { + margin: 0; + font-size: 1.6rem; + overflow-x: hidden; +} + +code { + font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important; + font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ +} + +/* Don't change font size in headers. */ +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + font-size: unset; +} + +.left { float: left; } +.right { float: right; } +.boring { opacity: 0.6; } +.hide-boring .boring { display: none; } +.hidden { display: none !important; } + +h2, h3 { margin-top: 2.5em; } +h4, h5 { margin-top: 2em; } + +.header + .header h3, +.header + .header h4, +.header + .header h5 { + margin-top: 1em; +} + +h1:target::before, +h2:target::before, +h3:target::before, +h4:target::before, +h5:target::before, +h6:target::before { + display: inline-block; + content: "»"; + margin-left: -30px; + width: 30px; +} + +/* This is broken on Safari as of version 14, but is fixed + in Safari Technology Preview 117 which I think will be Safari 14.2. + https://bugs.webkit.org/show_bug.cgi?id=218076 +*/ +:target { + scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); +} + +.page { + outline: 0; + padding: 0 var(--page-padding); + margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ +} +.page-wrapper { + box-sizing: border-box; +} +.js:not(.sidebar-resizing) .page-wrapper { + transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ +} + +.content { + overflow-y: auto; + padding: 0 15px; + padding-bottom: 50px; +} +.content main { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} +.content p { line-height: 1.45em; } +.content ol { line-height: 1.45em; } +.content ul { line-height: 1.45em; } +.content a { text-decoration: none; } +.content a:hover { text-decoration: underline; } +.content img, .content video { max-width: 100%; } +.content .header:link, +.content .header:visited { + color: var(--fg); +} +.content .header:link, +.content .header:visited:hover { + text-decoration: none; +} + +table { + margin: 0 auto; + border-collapse: collapse; +} +table td { + padding: 3px 20px; + border: 1px var(--table-border-color) solid; +} +table thead { + background: var(--table-header-bg); +} +table thead td { + font-weight: 700; + border: none; +} +table thead th { + padding: 3px 20px; +} +table thead tr { + border: 1px var(--table-header-bg) solid; +} +/* Alternate background colors for rows */ +table tbody tr:nth-child(2n) { + background: var(--table-alternate-bg); +} + + +blockquote { + margin: 20px 0; + padding: 0 20px; + color: var(--fg); + background-color: var(--quote-bg); + border-top: .1em solid var(--quote-border); + border-bottom: .1em solid var(--quote-border); +} + + +:not(.footnote-definition) + .footnote-definition, +.footnote-definition + :not(.footnote-definition) { + margin-top: 2em; +} +.footnote-definition { + font-size: 0.9em; + margin: 0.5em 0; +} +.footnote-definition p { + display: inline; +} + +.tooltiptext { + position: absolute; + visibility: hidden; + color: #fff; + background-color: #333; + transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ + left: -8px; /* Half of the width of the icon */ + top: -35px; + font-size: 0.8em; + text-align: center; + border-radius: 6px; + padding: 5px 8px; + margin: 5px; + z-index: 1000; +} +.tooltipped .tooltiptext { + visibility: visible; +} + +.chapter li.part-title { + color: var(--sidebar-fg); + margin: 5px 0px; + font-weight: bold; +} + +.result-no-output { + font-style: italic; +} diff --git a/css/print.css b/css/print.css new file mode 100644 index 000000000000..5e690f755994 --- /dev/null +++ b/css/print.css @@ -0,0 +1,54 @@ + +#sidebar, +#menu-bar, +.nav-chapters, +.mobile-nav-chapters { + display: none; +} + +#page-wrapper.page-wrapper { + transform: none; + margin-left: 0px; + overflow-y: initial; +} + +#content { + max-width: none; + margin: 0; + padding: 0; +} + +.page { + overflow-y: initial; +} + +code { + background-color: #666666; + border-radius: 5px; + + /* Force background to be printed in Chrome */ + -webkit-print-color-adjust: exact; +} + +pre > .buttons { + z-index: 2; +} + +a, a:visited, a:active, a:hover { + color: #4183c4; + text-decoration: none; +} + +h1, h2, h3, h4, h5, h6 { + page-break-inside: avoid; + page-break-after: avoid; +} + +pre, code { + page-break-inside: avoid; + white-space: pre-wrap; +} + +.fa { + display: none !important; +} diff --git a/css/variables.css b/css/variables.css new file mode 100644 index 000000000000..56b634bc3766 --- /dev/null +++ b/css/variables.css @@ -0,0 +1,253 @@ + +/* Globals */ + +:root { + --sidebar-width: 300px; + --page-padding: 15px; + --content-max-width: 750px; + --menu-bar-height: 50px; +} + +/* Themes */ + +.ayu { + --bg: hsl(210, 25%, 8%); + --fg: #c5c5c5; + + --sidebar-bg: #14191f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #5c6773; + --sidebar-active: #ffb454; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #0096cf; + + --inline-code-color: #ffb454; + + --theme-popup-bg: #14191f; + --theme-popup-border: #5c6773; + --theme-hover: #191f26; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(210, 25%, 13%); + --table-header-bg: hsl(210, 25%, 28%); + --table-alternate-bg: hsl(210, 25%, 11%); + + --searchbar-border-color: #848484; + --searchbar-bg: #424242; + --searchbar-fg: #fff; + --searchbar-shadow-color: #d4c89f; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #252932; + --search-mark-bg: #e3b171; +} + +.coal { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; +} + +.light { + --bg: hsl(0, 0%, 100%); + --fg: hsl(0, 0%, 0%); + + --sidebar-bg: #fafafa; + --sidebar-fg: hsl(0, 0%, 0%); + --sidebar-non-existant: #aaaaaa; + --sidebar-active: #1f1fff; + --sidebar-spacer: #f4f4f4; + + --scrollbar: #8F8F8F; + + --icons: #747474; + --icons-hover: #000000; + + --links: #20609f; + + --inline-code-color: #301900; + + --theme-popup-bg: #fafafa; + --theme-popup-border: #cccccc; + --theme-hover: #e6e6e6; + + --quote-bg: hsl(197, 37%, 96%); + --quote-border: hsl(197, 37%, 91%); + + --table-border-color: hsl(0, 0%, 95%); + --table-header-bg: hsl(0, 0%, 80%); + --table-alternate-bg: hsl(0, 0%, 97%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #e4f2fe; + --search-mark-bg: #a2cff5; +} + +.navy { + --bg: hsl(226, 23%, 11%); + --fg: #bcbdd0; + + --sidebar-bg: #282d3f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505274; + --sidebar-active: #2b79a2; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #161923; + --theme-popup-border: #737480; + --theme-hover: #282e40; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(226, 23%, 16%); + --table-header-bg: hsl(226, 23%, 31%); + --table-alternate-bg: hsl(226, 23%, 14%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #aeaec6; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #5f5f71; + --searchresults-border-color: #5c5c68; + --searchresults-li-bg: #242430; + --search-mark-bg: #a2cff5; +} + +.rust { + --bg: hsl(60, 9%, 87%); + --fg: #262625; + + --sidebar-bg: #3b2e2a; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505254; + --sidebar-active: #e69f67; + --sidebar-spacer: #45373a; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #262625; + + --links: #2b79a2; + + --inline-code-color: #6e6b5e; + + --theme-popup-bg: #e1e1db; + --theme-popup-border: #b38f6b; + --theme-hover: #99908a; + + --quote-bg: hsl(60, 5%, 75%); + --quote-border: hsl(60, 5%, 70%); + + --table-border-color: hsl(60, 9%, 82%); + --table-header-bg: #b3a497; + --table-alternate-bg: hsl(60, 9%, 84%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #dec2a2; + --search-mark-bg: #e69f67; +} + +@media (prefers-color-scheme: dark) { + .light.no-js { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; + } +} diff --git a/dev-assess.html b/dev-assess.html new file mode 100644 index 000000000000..46da07cef3c8 --- /dev/null +++ b/dev-assess.html @@ -0,0 +1,318 @@ + + + + + + cargo kani assess - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

cargo kani assess

+

Assess is an experimental new feature to gather data about Rust crates, to aid the start of proof writing.

+

In the short-term, assess collects and dumps tables of data that may help Kani developers understand what's needed to begin writing proofs for another project. +For instance, assess may help answer questions like:

+
    +
  1. Does Kani successfully build all of the crates involved in this project? If not, why not?
  2. +
  3. Does Kani support all the Rust language features necessary to do verification with this project? If not, which are most important?
  4. +
+

In the long-term, assess will become a user-facing feature, and help Kani users get started writing proofs. +We expect that users will have the same questions as above, but in the long term, hopefully the answers to those trend towards an uninteresting "yes." +So the new questions might be:

+
    +
  1. Is this project ready for verification? Projects need to be reasonably well-tested first. +Our operating hypothesis is that code currently covered by unit tests is the code that could become covered by proofs.
  2. +
  3. How much of given project (consisting of multiple packages or workspaces) or which of the user's projects might be verifiable? +If a user wants to start trying Kani, but they have the choice of several different packages where they might try, we can help find the package with the lowest hanging fruit.
  4. +
  5. Given a package, where in that package's code should the user look, in order to write the first (or next) proof?
  6. +
+

These long-term goals are only "hinted at" with the present experimental version of assess. +Currently, we only get as far as finding out which tests successfully verify (concretely) with Kani. +This might indicate tests that could be generalized and converted into proofs, but we currently don't do anything to group, rank, or otherwise heuristically prioritize what might be most "interesting." +(For instance, we'd like to eventually compute coverage information and use that to help rank the results.) +As a consequence, the output of the tool is very hard to interpret, and likely not (yet!) helpful to new or potential Kani users.

+

Using Assess

+

To assess a package, run:

+
cargo kani --enable-unstable assess
+
+

As a temporary hack (arguments shouldn't work like this), to assess a single cargo workspace, run:

+
cargo kani --enable-unstable --workspace assess
+
+

To scan a collection of workspaces or packages that are not part of a shared workspace, run:

+
cargo kani --enable-unstable assess scan
+
+

The only difference between 'scan' and 'regular' assess is how the packages built are located. +All versions of assess produce the same output and metrics. +Assess will normally build just like cargo kani or cargo build, whereas scan will find all cargo packages beneath the current directory, even in unrelated workspaces. +Thus, 'scan' may be helpful in the case where the user has a choice of packages and is looking for the easiest to get started with (in addition to the Kani developer use-case, of aggregating statistics across many packages).

+

(Tip: Assess may need to run for awhile, so try using screen, tmux or nohup to avoid terminating the process if, for example, an ssh connection breaks. +Some tests can also consume huge amounts of ram when run through Kani, so you may wish to use ulimit -v 6000000 to prevent any processes from using more than 6GB. +You can also limit the number of concurrent tests that will be run by providing e.g. -j 4, currently as a prepended argument, like --enable-unstable or --workspace in the examples above.)

+

What assess does

+

Assess builds all the packages requested in "test mode" (i.e. --tests), and runs all the same tests that cargo test would, except through Kani. +This gives end-to-end assurance we're able to actually build and run code from these packages, skipping nothing of what the verification process would need, except that the harnesses don't have any nondeterminism (kani::any()) and consequently don't "prove" much. +The interesting signal comes from what tests cannot be analyzed by Kani due to unsupported features, performance problems, crash bugs, or other issues that get in the way.

+

Currently, assess forces termination by using unwind(1) on all tests, so many tests will fail with unwinding assertions.

+

Current Assess Results

+

Assess produces a few tables of output (both visually in the terminal, and in a more detailed json format) so far:

+

Unsupported features

+
======================================================
+ Unsupported feature           |   Crates | Instances
+                               | impacted |    of use
+-------------------------------+----------+-----------
+ caller_location               |       71 |       239
+ simd_bitmask                  |       39 |       160
+...
+
+

The unsupported features table aggregates information about features that Kani does not yet support. +These correspond to uses of codegen_unimplemented in the kani-compiler, and appear as warnings during compilation.

+

Unimplemented features are not necessarily actually hit by (dynamically) reachable code, so an immediate future improvement on this table would be to count the features actually hit by failing test cases, instead of just those features reported as existing in code by the compiler. +In other words, the current unsupported features table is not what we want to see, in order to perfectly prioritize implementing these features, because we may be counting features that no proof would ever hit. +A perfect signal here isn't possible: there may be code that looks statically reachable, but is never dynamically reachable, and we can't tell. +But we can use test coverage as an approximation: well-tested code will hopefully cover most of the dynamically reachable code. +The operating hypothesis of assess is that code covered by tests is code that could be covered by proof, and so measuring unsupported features by those actually hit by a test should provide a better "signal" about priorities. +Implicitly deprioritizing unsupported features because they aren't covered by tests may not be a bug, but a feature: we may simply not want to prove anything about that code, if it hasn't been tested first, and so adding support for that feature may not be important.

+

A few notes on terminology:

+
    +
  1. "Crates impacted" here means "packages in the current workspace (or scan) where the building of that package (and all of its dependencies) ultimately resulted in this warning." +For example, if only assessing a single package (not a workspace) this could only be 1 in this column, regardless of the number of dependencies.
  2. +
  3. "Instances of use" likewise means "total instances found while compiling this package's tests and all the (reachable) code in its dependencies."
  4. +
  5. These counts are influenced by (static) reachability: if code is not potentially reachable from a test somehow, it will not be built and will not be counted.
  6. +
+

Test failure reasons

+
================================================
+ Reason for failure           | Number of tests
+------------------------------+-----------------
+ unwind                       |              61
+ none (success)               |               6
+ assertion + overflow         |               2
+...
+
+

The test failure reasons table indicates why, when assess ran a test through Kani, it failed to verify. +Notably:

+
    +
  1. Because we force termination with unwind(1), we expect unwind to rank highly.
  2. +
  3. We do report number of tests succeeding on this table, to aid understanding how well things went overall.
  4. +
  5. The reported reason is the "property class" of the CBMC property that failed. So assertion means an ordinary assert!() was hit (or something else with this property class).
  6. +
  7. When multiple properties fail, they are aggregated with +, such as assertion + overflow.
  8. +
  9. Currently this table does not properly account for should_fail tests, so assertion may actually be "success": the test should hit an assertion and did.
  10. +
+

Promising test cases

+
=============================================================================
+ Candidate for proof harness                           | Location
+-------------------------------------------------------+---------------------
+ float::tests::f64_edge_cases                          | src/float.rs:226
+ float::tests::f32_edge_cases                          | src/float.rs:184
+ integer::tests::test_integers                         | src/integer.rs:171
+
+

This table is the most rudimentary so far, but is the core of what long-term assess will help accomplish. +Currently, this table just presents (with paths displayed in a clickable manner) the tests that successfully "verify" with Kani. +These might be good candidates for turning into proof harnesses. +This list is presently unordered; the next step for improving it would be to find even a rudimentary way of ranking these test cases (e.g. perhaps by code coverage).

+

How Assess Works

+

kani-compiler emits *.kani-metadata.json for each target it builds. +This format can be found in the kani_metadata crate, shared by kani-compiler and kani-driver. +This is the starting point for assess.

+

Assess obtains this metadata by essentially running a cargo kani:

+
    +
  1. With --all-features turned on
  2. +
  3. With unwind always set to 1
  4. +
  5. In test mode, i.e. --tests
  6. +
  7. With test-case reachability mode. Normally Kani looks for proof harnesses and builds only those. Here we switch to building only the test harnesses instead.
  8. +
+

Assess starts by getting all the information from these metadata files. +This is enough by itself to construct a rudimentary "unsupported features" table. +But assess also uses it to discover all the test cases, and (instead of running proof harnesses) it then runs all these test harnesses under Kani.

+

Assess produces a second metadata format, called (unsurprisingly) "assess metadata". +(Found in kani-driver under src/assess/metadata.rs.) +This format records the results of what assess does.

+

This metadata can be written to a json file by providing --emit-metadata <file> to assess. +Likewise, scan can be told to write out this data with the same option.

+

Assess metadata is an aggregatable format. +It does not apply to just one package, as assess can work on a workspace of packages. +Likewise, scan uses and produces the exact same format, across multiple workspaces.

+

So far all assess metadata comes in the form of "tables" which are built with TableBuilder<T: TableRow>. +This is documented further in src/assess/table_builder.rs.

+

Using Assess on the top-100 crates

+

There is a script in the Kani repo for this purpose.

+

This will clone the top-100 crates to /tmp/top-100-experiment and run assess scan on them:

+
./scripts/exps/assess-scan-on-repos.sh
+
+

If you'd like to preseve the results, you can direct scan to use a different directory with an environment variable:

+
ASSESS_SCAN="~/top-100-experiment" ./scripts/exps/assess-scan-on-repos.sh
+
+

To re-run the experiment, it suffices to be in the experiment directory:

+
cd ~/top-100-experiment && ~/kani/scripts/exps/assess-scan-on-repos.sh
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/dev-documentation.html b/dev-documentation.html new file mode 100644 index 000000000000..a3d80c5e21f4 --- /dev/null +++ b/dev-documentation.html @@ -0,0 +1,204 @@ + + + + + + Developer documentation - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Developer documentation

+

Kani is an open source project open to external contributions.

+

The easiest way to contribute is to report any +issue you encounter +while using the tool. If you want to contribute to its development, +we recommend looking into these issues.

+

In this chapter, we provide documentation that might be helpful for Kani +developers (including external contributors):

+
    +
  1. Coding conventions.
  2. +
  3. Useful command-line instructions for Kani/CBMC/Git.
  4. +
  5. Development setup recommendations for working with cbmc.
  6. +
  7. Development setup recommendations for working with rustc.
  8. +
  9. Guide for testing in Kani.
  10. +
  11. Transition to StableMIR.
  12. +
+
+

NOTE: The developer documentation is intended for Kani developers and not +users. At present, the project is under heavy development and some items +discussed in this documentation may stop working without notice (e.g., commands +or configurations). Therefore, we recommend users to not rely on them.

+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/elasticlunr.min.js b/elasticlunr.min.js new file mode 100644 index 000000000000..94b20dd2ef46 --- /dev/null +++ b/elasticlunr.min.js @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5 + * + * Copyright (C) 2017 Oliver Nightingale + * Copyright (C) 2017 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o + + + + + FAQ - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

FAQs

+

This section collects frequently asked questions about Kani. +Please consider opening an issue if you have a question that would like to see here.

+

Questions

+
+Kani doesn't fail after kani::assume(false). Why? +
+

kani::assume(false) (or kani::assume(cond) where cond is a condition that results in false in the context of the program), won't cause errors in Kani. +Instead, such an assumption has the effect of blocking all the symbolic execution paths from the assumption. +Therefore, all checks after the assumption should appear as UNREACHABLE. +That's the expected behavior for kani::assume(false) in Kani.

+

If you didn't expect certain checks in a harness to be UNREACHABLE, we recommend using the kani::cover macro to determine what conditions are possible in case you've over-constrained the harness.

+
+
+I implemented the kani::Arbitrary trait for a type that's not from my crate, and got the error +only traits defined in the current crate can be implemented for types defined outside of the crate. +What does this mean? What can I do? +
+

This error is due to a violation of Rust's orphan rules for trait implementations, which are explained here. +In that case, you'll need to write a function that builds an object from non-deterministic variables. +Inside this function you would simply return an arbitrary value by generating arbitrary values for its components.

+

For example, let's assume the type you're working with is this enum:

+
#[derive(Copy, Clone)]
+pub enum Rating {
+    One,
+    Two,
+    Three,
+}
+
+

Then, you can match on a non-deterministic integer (supplied by kani::any) to return non-deterministic Rating variants:

+
    pub fn any_rating() -> Rating {
+        match kani::any() {
+            0 => Rating::One,
+            1 => Rating::Two,
+            _ => Rating::Three,
+        }
+    }
+
+

More details about this option, which also useful in other cases, can be found here.

+

If the type comes from std (Rust's standard library), you can open a request for adding Arbitrary implementations to the Kani library. +Otherwise, there are more involved options to consider:

+
    +
  1. Importing a copy of the external crate that defines the type, then implement Arbitrary there.
  2. +
  3. Contributing the Arbitrary implementation to the external crate that defines the type.
  4. +
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/favicon.png b/favicon.png new file mode 100644 index 000000000000..a5b1aa16c4dc Binary files /dev/null and b/favicon.png differ diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 000000000000..90e0ea58bdb1 --- /dev/null +++ b/favicon.svg @@ -0,0 +1,22 @@ + + + + + diff --git a/fonts/OPEN-SANS-LICENSE.txt b/fonts/OPEN-SANS-LICENSE.txt new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/fonts/OPEN-SANS-LICENSE.txt @@ -0,0 +1,202 @@ + + 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/fonts/SOURCE-CODE-PRO-LICENSE.txt b/fonts/SOURCE-CODE-PRO-LICENSE.txt new file mode 100644 index 000000000000..366206f54950 --- /dev/null +++ b/fonts/SOURCE-CODE-PRO-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/fonts.css b/fonts/fonts.css new file mode 100644 index 000000000000..858efa59800b --- /dev/null +++ b/fonts/fonts.css @@ -0,0 +1,100 @@ +/* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */ +/* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */ + +/* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + src: local('Open Sans Light'), local('OpenSans-Light'), + url('open-sans-v17-all-charsets-300.woff2') format('woff2'); +} + +/* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), + url('open-sans-v17-all-charsets-300italic.woff2') format('woff2'); +} + +/* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans Regular'), local('OpenSans-Regular'), + url('open-sans-v17-all-charsets-regular.woff2') format('woff2'); +} + +/* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), + url('open-sans-v17-all-charsets-italic.woff2') format('woff2'); +} + +/* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), + url('open-sans-v17-all-charsets-600.woff2') format('woff2'); +} + +/* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), + url('open-sans-v17-all-charsets-600italic.woff2') format('woff2'); +} + +/* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), + url('open-sans-v17-all-charsets-700.woff2') format('woff2'); +} + +/* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), + url('open-sans-v17-all-charsets-700italic.woff2') format('woff2'); +} + +/* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 800; + src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), + url('open-sans-v17-all-charsets-800.woff2') format('woff2'); +} + +/* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 800; + src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), + url('open-sans-v17-all-charsets-800italic.woff2') format('woff2'); +} + +/* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 500; + src: url('source-code-pro-v11-all-charsets-500.woff2') format('woff2'); +} diff --git a/fonts/open-sans-v17-all-charsets-300.woff2 b/fonts/open-sans-v17-all-charsets-300.woff2 new file mode 100644 index 000000000000..9f51be370fa9 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-300.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-300italic.woff2 b/fonts/open-sans-v17-all-charsets-300italic.woff2 new file mode 100644 index 000000000000..2f545448418c Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-300italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-600.woff2 b/fonts/open-sans-v17-all-charsets-600.woff2 new file mode 100644 index 000000000000..f503d558d58d Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-600.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-600italic.woff2 b/fonts/open-sans-v17-all-charsets-600italic.woff2 new file mode 100644 index 000000000000..c99aabe80340 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-600italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-700.woff2 b/fonts/open-sans-v17-all-charsets-700.woff2 new file mode 100644 index 000000000000..421a1ab25fa8 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-700.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-700italic.woff2 b/fonts/open-sans-v17-all-charsets-700italic.woff2 new file mode 100644 index 000000000000..12ce3d20d1ce Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-700italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-800.woff2 b/fonts/open-sans-v17-all-charsets-800.woff2 new file mode 100644 index 000000000000..c94a223b0332 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-800.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-800italic.woff2 b/fonts/open-sans-v17-all-charsets-800italic.woff2 new file mode 100644 index 000000000000..eed7d3c63d1f Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-800italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-italic.woff2 b/fonts/open-sans-v17-all-charsets-italic.woff2 new file mode 100644 index 000000000000..398b68a0853f Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-regular.woff2 b/fonts/open-sans-v17-all-charsets-regular.woff2 new file mode 100644 index 000000000000..8383e94c6547 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-regular.woff2 differ diff --git a/fonts/source-code-pro-v11-all-charsets-500.woff2 b/fonts/source-code-pro-v11-all-charsets-500.woff2 new file mode 100644 index 000000000000..722245682f59 Binary files /dev/null and b/fonts/source-code-pro-v11-all-charsets-500.woff2 differ diff --git a/getting-started.html b/getting-started.html new file mode 100644 index 000000000000..d30f3bceafec --- /dev/null +++ b/getting-started.html @@ -0,0 +1,195 @@ + + + + + + Getting started - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Getting started

+

Kani is an open-source verification tool that uses model checking to analyze Rust programs. +Kani is particularly useful for verifying unsafe code blocks in Rust, where the "unsafe superpowers" are unchecked by the compiler. +Some example properties you can prove with Kani include memory safety properties (e.g., null pointer dereferences, use-after-free, etc.), the absence of certain runtime errors (i.e., index out of bounds, panics), and the absence of some types of unexpected behavior (e.g., arithmetic overflows). +Kani can also prove custom properties provided in the form of user-specified assertions. +As Kani uses model checking, Kani will either prove the property, disprove the +property (with a counterexample), or may run out of resources.

+

Kani uses proof harnesses to analyze programs. +Proof harnesses are similar to test harnesses, especially property-based test harnesses.

+

Project Status

+

Kani is currently under active development. +Releases are published here. +Major changes to Kani are documented in the RFC Book. +We also publish updates on Kani use cases and features on our blog.

+

There is support for a fair amount of Rust language features, but not all (e.g., concurrency). +Please see Limitations for a detailed list of supported features.

+

Kani releases every two weeks. +As part of every release, Kani will synchronize with a recent nightly release of Rust, and so is generally up-to-date with the latest Rust language features.

+

If you encounter issues when using Kani, we encourage you to report them to us.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/getting-started/verification-results/Cargo.toml b/getting-started/verification-results/Cargo.toml new file mode 100644 index 000000000000..896ce98400a0 --- /dev/null +++ b/getting-started/verification-results/Cargo.toml @@ -0,0 +1,14 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "result-types" +version = "0.1.0" +edition = "2018" + +[dependencies] + +[workspace] + +[package.metadata.kani] +flags = {output-format = "regular"} diff --git a/getting-started/verification-results/failure_example.expected b/getting-started/verification-results/failure_example.expected new file mode 100644 index 000000000000..7df34043cdf7 --- /dev/null +++ b/getting-started/verification-results/failure_example.expected @@ -0,0 +1,2 @@ +FAILURE\ +Description: "assertion failed: arr.len() != 3" diff --git a/getting-started/verification-results/src/main.rs b/getting-started/verification-results/src/main.rs new file mode 100644 index 000000000000..7a03b34f0f9e --- /dev/null +++ b/getting-started/verification-results/src/main.rs @@ -0,0 +1,96 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +// ANCHOR: success_example +fn success_example() { + let mut sum = 0; + for i in 1..4 { + sum += i; + } + assert_eq!(sum, 6); +} +// ANCHOR_END: success_example + +#[kani::proof] +// ANCHOR: failure_example +fn failure_example() { + let arr = [1, 2, 3]; + assert_ne!(arr.len(), 3); +} +// ANCHOR_END: failure_example + +#[kani::proof] +// ANCHOR: unreachable_example +fn unreachable_example() { + let x = 5; + let y = x + 2; + if x > y { + assert!(x < 8); + } +} +// ANCHOR_END: unreachable_example + +#[kani::proof] +// ANCHOR: undetermined_example +fn undetermined_example() { + let mut x = 0; + unsupp(&mut x); + assert!(x == 0); +} + +#[feature(asm)] +fn unsupp(x: &mut u8) { + unsafe { + std::arch::asm!("nop"); + } +} + +// ANCHOR_END: undetermined_example + +#[kani::proof] +// ANCHOR: cover_satisfied_example +#[kani::unwind(256)] +fn cover_satisfied_example() { + let mut x: u8 = kani::any(); + let mut y: u8 = kani::any(); + y /= 2; + let mut i = 0; + while x != 0 && y != 0 { + kani::cover!(i > 2 && x == 24 && y == 72); + if x >= y { x -= y; } + else { y -= x; } + i += 1; + } +} +// ANCHOR_END: cover_satisfied_example + +#[kani::proof] +// ANCHOR: cover_unsatisfiable_example +#[kani::unwind(6)] +fn cover_unsatisfiable_example() { + let bytes: [u8; 5] = kani::any(); + let s = std::str::from_utf8(&bytes); + if let Ok(s) = s { + kani::cover!(s.chars().count() <= 1); + } +} +// ANCHOR_END: cover_unsatisfiable_example + +#[kani::proof] +// ANCHOR: cover_unreachable_example +#[kani::unwind(6)] +fn cover_unreachable_example() { + let r1: std::ops::Range = kani::any()..kani::any(); + let r2: std::ops::Range = kani::any()..kani::any(); + kani::assume(!r1.is_empty()); + kani::assume(!r2.is_empty()); + if r2.start > r1.end { + if r2.end < r1.end { + kani::cover!(r2.contains(&0)); + } + } +} +// ANCHOR_END: cover_unreachable_example + +fn main() {} diff --git a/getting-started/verification-results/success_example.expected b/getting-started/verification-results/success_example.expected new file mode 100644 index 000000000000..65cb0eb8b288 --- /dev/null +++ b/getting-started/verification-results/success_example.expected @@ -0,0 +1,2 @@ +SUCCESS\ +Description: "assertion failed: sum == 6" diff --git a/getting-started/verification-results/undetermined_example.expected b/getting-started/verification-results/undetermined_example.expected new file mode 100644 index 000000000000..273ef21610de --- /dev/null +++ b/getting-started/verification-results/undetermined_example.expected @@ -0,0 +1,2 @@ +UNDETERMINED\ +Description: "assertion failed: x == 0" diff --git a/getting-started/verification-results/unreachable_example.expected b/getting-started/verification-results/unreachable_example.expected new file mode 100644 index 000000000000..9beb5ee416fa --- /dev/null +++ b/getting-started/verification-results/unreachable_example.expected @@ -0,0 +1,2 @@ +UNREACHABLE\ +Description: "assertion failed: x < 8" diff --git a/highlight.css b/highlight.css new file mode 100644 index 000000000000..c2343227201e --- /dev/null +++ b/highlight.css @@ -0,0 +1,83 @@ +/* + * An increased contrast highlighting scheme loosely based on the + * "Base16 Atelier Dune Light" theme by Bram de Haan + * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) + * Original Base16 color scheme by Chris Kempson + * (https://github.com/chriskempson/base16) + */ + +/* Comment */ +.hljs-comment, +.hljs-quote { + color: #575757; +} + +/* Red */ +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-tag, +.hljs-name, +.hljs-regexp, +.hljs-link, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class { + color: #d70025; +} + +/* Orange */ +.hljs-number, +.hljs-meta, +.hljs-built_in, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #b21e00; +} + +/* Green */ +.hljs-string, +.hljs-symbol, +.hljs-bullet { + color: #008200; +} + +/* Blue */ +.hljs-title, +.hljs-section { + color: #0030f2; +} + +/* Purple */ +.hljs-keyword, +.hljs-selector-tag { + color: #9d00ec; +} + +.hljs { + display: block; + overflow-x: auto; + background: #f6f7f6; + color: #000; + padding: 0.5em; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #22863a; + background-color: #f0fff4; +} + +.hljs-deletion { + color: #b31d28; + background-color: #ffeef0; +} diff --git a/highlight.js b/highlight.js new file mode 100644 index 000000000000..180385b7028f --- /dev/null +++ b/highlight.js @@ -0,0 +1,6 @@ +/* + Highlight.js 10.1.1 (93fd0d73) + License: BSD-3-Clause + Copyright (c) 2006-2020, Ivan Sagalaev +*/ +var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="
",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("php",function(){"use strict";return function(e){var r={begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{aliases:["php","php3","php4","php5","php6","php7"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler"}),{className:"string",begin:/<<<['"]?\w+['"]?$/,end:/^\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]}]},t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},a,n]}}}());hljs.registerLanguage("nginx",function(){"use strict";return function(e){var n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/}/},{begin:"[\\$\\@]"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{$pattern:"[a-z/_]+",literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+"\\s+{",returnBegin:!0,end:"{",contains:[{className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|{",returnBegin:!0,contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:"[^\\s\\}]"}}}());hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}());hljs.registerLanguage("perl",function(){"use strict";return function(e){var n={$pattern:/[\w.]+/,keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when"},t={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:n},s={begin:"->{",end:"}"},r={variants:[{begin:/\$\d/},{begin:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{begin:/[\$%@][^\s\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT("^\\=\\w","\\=cut",{endsWithParent:!0}),s,{className:"string",contains:i,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*\\<",end:"\\>",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:"{\\w+}",contains:[],relevance:0},{begin:"-?\\w+\\s*\\=\\>",contains:[],relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",begin:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",relevance:10},{className:"regexp",begin:"(m|qr)?/",end:"/[a-z]*",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return t.contains=a,s.contains=a,{name:"Perl",aliases:["pl","pm"],keywords:n,contains:a}}}());hljs.registerLanguage("swift",function(){"use strict";return function(e){var i={keyword:"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c compactMap contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},n=e.COMMENT("/\\*","\\*/",{contains:["self"]}),t={className:"subst",begin:/\\\(/,end:"\\)",keywords:i,contains:[]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},r={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};return t.contains=[r],{name:"Swift",keywords:i,contains:[a,e.C_LINE_COMMENT_MODE,n,{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*[!?]"},{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*",relevance:0},r,{className:"function",beginKeywords:"func",end:"{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin://},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:i,contains:["self",r,a,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:i,end:"\\{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)\\b"},{beginKeywords:"import",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}());hljs.registerLanguage("makefile",function(){"use strict";return function(e){var i={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:")",end:">",keywords:{name:"style"},contains:[c],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}());hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b-?[a-z\._]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}());hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},i={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},c=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}());hljs.registerLanguage("coffeescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((e=>n=>!e.includes(n))(["var","const","let","function","static"])).join(" "),literal:n.concat(["yes","no","on","off"]).join(" "),built_in:a.concat(["npm","print"]).join(" ")},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:t,contains:["self"].concat(o)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/,contains:o.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[c,l]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[l]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[c]},c]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}());hljs.registerLanguage("ruby",function(){"use strict";return function(e){var n="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",a={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},i={begin:"#<",end:">"},r=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^\\=begin","^\\=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],c={className:"subst",begin:"#\\{",end:"}",keywords:a},t={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<[-~]?'?(\w+)(?:.|\n)*?\n\s*\1\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},b={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:a},d=[t,i,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(r)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[t,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:a},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[i,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\s*=>/,starts:{end:"$",contains:d}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:d}}];return{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:a,illegal:/\/\*/,contains:r.concat(g).concat(d)}}}());hljs.registerLanguage("yaml",function(){"use strict";return function(e){var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*\\'()[\\]]+",s={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]},i=e.inherit(s,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={begin:"{",end:"}",contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"\\-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b"},t,g,s],c=[...b];return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml","YAML"],contains:b}}}());hljs.registerLanguage("d",function(){"use strict";return function(e){var a={$pattern:e.UNDERSCORE_IDENT_RE,keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},d="((0|[1-9][\\d_]*)|0[bB][01_]+|0[xX]([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))",n="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",t={className:"number",begin:"\\b"+d+"(L|u|U|Lu|LU|uL|UL)?",relevance:0},_={className:"number",begin:"\\b(((0[xX](([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)\\.([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)|\\.?([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))[pP][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))|((0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(\\.\\d*|([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)))|\\d+\\.(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)|\\.(0|[1-9][\\d_]*)([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))?))([fF]|L|i|[fF]i|Li)?|"+d+"(i|[fF]i|Li))",relevance:0},r={className:"string",begin:"'("+n+"|.)",end:"'",illegal:"."},i={className:"string",begin:'"',contains:[{begin:n,relevance:0}],end:'"[cwd]?'},s=e.COMMENT("\\/\\+","\\+\\/",{contains:["self"],relevance:10});return{name:"D",keywords:a,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{className:"string",begin:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',relevance:10},i,{className:"string",begin:'[rq]"',end:'"[cwd]?',relevance:5},{className:"string",begin:"`",end:"`[cwd]?"},{className:"string",begin:'q"\\{',end:'\\}"'},_,t,r,{className:"meta",begin:"^#!",end:"$",relevance:5},{className:"meta",begin:"#(line)",end:"$",relevance:5},{className:"keyword",begin:"@[a-zA-Z_][a-zA-Z_\\d]*"}]}}}());hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}());hljs.registerLanguage("http",function(){"use strict";return function(e){var n="HTTP/[0-9\\.]+";return{name:"HTTP",aliases:["https"],illegal:"\\S",contains:[{begin:"^"+n,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+n+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:n},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}}());hljs.registerLanguage("haskell",function(){"use strict";return function(e){var n={variants:[e.COMMENT("--","$"),e.COMMENT("{-","-}",{contains:["self"]})]},i={className:"meta",begin:"{-#",end:"#-}"},a={className:"meta",begin:"^#",end:"$"},s={className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},l={begin:"\\(",end:"\\)",illegal:'"',contains:[i,a,{className:"type",begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TITLE_MODE,{begin:"[_a-z][\\w']*"}),n]};return{name:"Haskell",aliases:["hs"],keywords:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",contains:[{beginKeywords:"module",end:"where",keywords:"module where",contains:[l,n],illegal:"\\W\\.|;"},{begin:"\\bimport\\b",end:"$",keywords:"import qualified as hiding",contains:[l,n],illegal:"\\W\\.|;"},{className:"class",begin:"^(\\s*)?(class|instance)\\b",end:"where",keywords:"class family instance where",contains:[s,l,n]},{className:"class",begin:"\\b(data|(new)?type)\\b",end:"$",keywords:"data family type newtype deriving",contains:[i,s,l,{begin:"{",end:"}",contains:l.contains},n]},{beginKeywords:"default",end:"$",contains:[s,l,n]},{beginKeywords:"infix infixl infixr",end:"$",contains:[e.C_NUMBER_MODE,n]},{begin:"\\bforeign\\b",end:"$",keywords:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",contains:[s,e.QUOTE_STRING_MODE,n]},{className:"meta",begin:"#!\\/usr\\/bin\\/env runhaskell",end:"$"},i,a,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,s,e.inherit(e.TITLE_MODE,{begin:"^[_a-z][\\w']*"}),n,{begin:"->|<-"}]}}}());hljs.registerLanguage("handlebars",function(){"use strict";function e(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(n){const a={"builtin-name":"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield"},t=/\[.*?\]/,s=/[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/,i=e("(",/'.*?'/,"|",/".*?"/,"|",t,"|",s,"|",/\.|\//,")+"),r=e("(",t,"|",s,")(?==)"),l={begin:i,lexemes:/[\w.\/]+/},c=n.inherit(l,{keywords:{literal:"true false undefined null"}}),o={begin:/\(/,end:/\)/},m={className:"attr",begin:r,relevance:0,starts:{begin:/=/,end:/=/,starts:{contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,c,o]}}},d={contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,{begin:/as\s+\|/,keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]},m,c,o],returnEnd:!0},g=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/\)/})});o.contains=[g];const u=n.inherit(l,{keywords:a,className:"name",starts:n.inherit(d,{end:/}}/})}),b=n.inherit(l,{keywords:a,className:"name"}),h=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/}}/})});return{name:"Handlebars",aliases:["hbs","html.hbs","html.handlebars","htmlbars"],case_insensitive:!0,subLanguage:"xml",contains:[{begin:/\\\{\{/,skip:!0},{begin:/\\\\(?=\{\{)/,skip:!0},n.COMMENT(/\{\{!--/,/--\}\}/),n.COMMENT(/\{\{!/,/\}\}/),{className:"template-tag",begin:/\{\{\{\{(?!\/)/,end:/\}\}\}\}/,contains:[u],starts:{end:/\{\{\{\{\//,returnEnd:!0,subLanguage:"xml"}},{className:"template-tag",begin:/\{\{\{\{\//,end:/\}\}\}\}/,contains:[b]},{className:"template-tag",begin:/\{\{#/,end:/\}\}/,contains:[u]},{className:"template-tag",begin:/\{\{(?=else\}\})/,end:/\}\}/,keywords:"else"},{className:"template-tag",begin:/\{\{\//,end:/\}\}/,contains:[b]},{className:"template-variable",begin:/\{\{\{/,end:/\}\}\}/,contains:[h]},{className:"template-variable",begin:/\{\{/,end:/\}\}/,contains:[h]}]}}}());hljs.registerLanguage("rust",function(){"use strict";return function(e){var n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield",literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}}());hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}());hljs.registerLanguage("ini",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(...n){return n.map(n=>e(n)).join("")}return function(a){var s={className:"number",relevance:0,variants:[{begin:/([\+\-]+)?[\d]+_[\d_]+/},{begin:a.NUMBER_RE}]},i=a.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var t={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={className:"string",contains:[a.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,s,"self"],relevance:0},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map(n=>e(n)).join("|")+")";return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr",starts:{end:/$/,contains:[i,c,r,t,l,s]}}]}}}());hljs.registerLanguage("objectivec",function(){"use strict";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n,keyword:"@interface @class @protocol @implementation"};return{name:"Objective-C",aliases:["mm","objc","obj-c"],keywords:{$pattern:n,keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:"({|$)",excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}());hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}());hljs.registerLanguage("java",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(e){return a("(",e,")?")}function a(...n){return n.map(n=>e(n)).join("")}function s(...n){return"("+n.map(n=>e(n)).join("|")+")"}return function(e){var t="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",i={className:"meta",begin:"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},r=e=>a("[",e,"]+([",e,"_]*[",e,"]+)?"),c={className:"number",variants:[{begin:`\\b(0[bB]${r("01")})[lL]?`},{begin:`\\b(0${r("0-7")})[dDfFlL]?`},{begin:a(/\b0[xX]/,s(a(r("a-fA-F0-9"),/\./,r("a-fA-F0-9")),a(r("a-fA-F0-9"),/\.?/),a(/\./,r("a-fA-F0-9"))),/([pP][+-]?(\d+))?/,/[fFdDlL]?/)},{begin:a(/\b/,s(a(/\d*\./,r("\\d")),r("\\d")),/[eE][+-]?[\d]+[dDfF]?/)},{begin:a(/\b/,r(/\d/),n(/\.?/),n(r(/\d/)),/[dDfFlL]?/)}],relevance:0};return{name:"Java",aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\s*,\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c,i]}}}());hljs.registerLanguage("x86asm",function(){"use strict";return function(s){return{name:"Intel x86 Assembly",case_insensitive:!0,keywords:{$pattern:"[.%]?"+s.IDENT_RE,keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63",built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr",meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},contains:[s.COMMENT(";","$",{relevance:0}),{className:"number",variants:[{begin:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",relevance:0},{begin:"\\$[0-9][0-9A-Fa-f]*",relevance:0},{begin:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{begin:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"}]},s.QUOTE_STRING_MODE,{className:"string",variants:[{begin:"'",end:"[^\\\\]'"},{begin:"`",end:"[^\\\\]`"}],relevance:0},{className:"symbol",variants:[{begin:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{begin:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],relevance:0},{className:"subst",begin:"%[0-9]+",relevance:0},{className:"subst",begin:"%!S+",relevance:0},{className:"meta",begin:/^\s*\.[\w_-]+/}]}}}());hljs.registerLanguage("kotlin",function(){"use strict";return function(e){var n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:"\\${",end:"}",contains:[e.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},t={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(t,{className:"meta-string"})]}]},c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},a,r,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0}]}}}());hljs.registerLanguage("armasm",function(){"use strict";return function(s){const e={variants:[s.COMMENT("^[ \\t]*(?=#)","$",{relevance:0,excludeBegin:!0}),s.COMMENT("[;@]","$",{relevance:0}),s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE]};return{name:"ARM Assembly",case_insensitive:!0,aliases:["arm"],keywords:{$pattern:"\\.?"+s.IDENT_RE,meta:".2byte .4byte .align .ascii .asciz .balign .byte .code .data .else .end .endif .endm .endr .equ .err .exitm .extern .global .hword .if .ifdef .ifndef .include .irp .long .macro .rept .req .section .set .skip .space .text .word .arm .thumb .code16 .code32 .force_thumb .thumb_func .ltorg ALIAS ALIGN ARM AREA ASSERT ATTR CN CODE CODE16 CODE32 COMMON CP DATA DCB DCD DCDU DCDO DCFD DCFDU DCI DCQ DCQU DCW DCWU DN ELIF ELSE END ENDFUNC ENDIF ENDP ENTRY EQU EXPORT EXPORTAS EXTERN FIELD FILL FUNCTION GBLA GBLL GBLS GET GLOBAL IF IMPORT INCBIN INCLUDE INFO KEEP LCLA LCLL LCLS LTORG MACRO MAP MEND MEXIT NOFP OPT PRESERVE8 PROC QN READONLY RELOC REQUIRE REQUIRE8 RLIST FN ROUT SETA SETL SETS SN SPACE SUBT THUMB THUMBX TTL WHILE WEND ",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 pc lr sp ip sl sb fp a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8 f0 f1 f2 f3 f4 f5 f6 f7 p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 cpsr_c cpsr_x cpsr_s cpsr_f cpsr_cx cpsr_cxs cpsr_xs cpsr_xsf cpsr_sf cpsr_cxsf spsr_c spsr_x spsr_s spsr_f spsr_cx spsr_cxs spsr_xs spsr_xsf spsr_sf spsr_cxsf s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 {PC} {VAR} {TRUE} {FALSE} {OPT} {CONFIG} {ENDIAN} {CODESIZE} {CPU} {FPU} {ARCHITECTURE} {PCSTOREOFFSET} {ARMASM_VERSION} {INTER} {ROPI} {RWPI} {SWST} {NOSWST} . @"},contains:[{className:"keyword",begin:"\\b(adc|(qd?|sh?|u[qh]?)?add(8|16)?|usada?8|(q|sh?|u[qh]?)?(as|sa)x|and|adrl?|sbc|rs[bc]|asr|b[lx]?|blx|bxj|cbn?z|tb[bh]|bic|bfc|bfi|[su]bfx|bkpt|cdp2?|clz|clrex|cmp|cmn|cpsi[ed]|cps|setend|dbg|dmb|dsb|eor|isb|it[te]{0,3}|lsl|lsr|ror|rrx|ldm(([id][ab])|f[ds])?|ldr((s|ex)?[bhd])?|movt?|mvn|mra|mar|mul|[us]mull|smul[bwt][bt]|smu[as]d|smmul|smmla|mla|umlaal|smlal?([wbt][bt]|d)|mls|smlsl?[ds]|smc|svc|sev|mia([bt]{2}|ph)?|mrr?c2?|mcrr2?|mrs|msr|orr|orn|pkh(tb|bt)|rbit|rev(16|sh)?|sel|[su]sat(16)?|nop|pop|push|rfe([id][ab])?|stm([id][ab])?|str(ex)?[bhd]?|(qd?)?sub|(sh?|q|u[qh]?)?sub(8|16)|[su]xt(a?h|a?b(16)?)|srs([id][ab])?|swpb?|swi|smi|tst|teq|wfe|wfi|yield)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo)?[sptrx]?(?=\\s)"},e,s.QUOTE_STRING_MODE,{className:"string",begin:"'",end:"[^\\\\]'",relevance:0},{className:"title",begin:"\\|",end:"\\|",illegal:"\\n",relevance:0},{className:"number",variants:[{begin:"[#$=]?0x[0-9a-f]+"},{begin:"[#$=]?0b[01]+"},{begin:"[#$=]\\d+"},{begin:"\\b\\d+"}],relevance:0},{className:"symbol",variants:[{begin:"^[ \\t]*[a-z_\\.\\$][a-z0-9_\\.\\$]+:"},{begin:"^[a-z_\\.\\$][a-z0-9_\\.\\$]+"},{begin:"[=#]\\w+"}],relevance:0}]}}}());hljs.registerLanguage("go",function(){"use strict";return function(e){var n={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{name:"Go",aliases:["golang"],keywords:n,illegal:">>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}());hljs.registerLanguage("shell",function(){"use strict";return function(s){return{name:"Shell Session",aliases:["console"],contains:[{className:"meta",begin:"^\\s{0,3}[/\\w\\d\\[\\]()@-]*[>%$#]",starts:{end:"$",subLanguage:"bash"}}]}}}());hljs.registerLanguage("scala",function(){"use strict";return function(e){var n={className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"},{begin:"\\${",end:"}"}]},a={className:"string",variants:[{begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:'"""',end:'"""',relevance:10},{begin:'[a-z]+"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n]},{className:"string",begin:'[a-z]+"""',end:'"""',contains:[n],relevance:10}]},s={className:"type",begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={className:"title",begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,relevance:0},i={className:"class",beginKeywords:"class object trait type",end:/[:={\[\n;]/,excludeEnd:!0,contains:[{beginKeywords:"extends with",relevance:10},{begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},t]},l={className:"function",beginKeywords:"def",end:/[:={\[(\n;]/,excludeEnd:!0,contains:[t]};return{name:"Scala",keywords:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,{className:"symbol",begin:"'\\w[\\w\\d_]*(?!')"},s,l,i,e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"}]}}}());hljs.registerLanguage("julia",function(){"use strict";return function(e){var r="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",t={$pattern:r,keyword:"in isa where baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import importall let local macro module quote return true try using while type immutable abstract bitstype typealias ",literal:"true false ARGS C_NULL DevNull ENDIAN_BOM ENV I Inf Inf16 Inf32 Inf64 InsertionSort JULIA_HOME LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp STDERR STDIN STDOUT VERSION catalan e|0 eu|0 eulergamma golden im nothing pi γ π φ ",built_in:"ANY AbstractArray AbstractChannel AbstractFloat AbstractMatrix AbstractRNG AbstractSerializer AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError Associative Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError BufferStream CachingPool CapturedException CartesianIndex CartesianRange Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong ClusterManager Cmd CodeInfo Colon Complex Complex128 Complex32 Complex64 CompositeException Condition ConjArray ConjMatrix ConjVector Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring DataType Date DateFormat DateTime DenseArray DenseMatrix DenseVecOrMat DenseVector Diagonal Dict DimensionMismatch Dims DirectIndexString Display DivideError DomainError EOFError EachLine Enum Enumerate ErrorException Exception ExponentialBackOff Expr Factorization FileMonitor Float16 Float32 Float64 Function Future GlobalRef GotoNode HTML Hermitian IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException InvalidStateException Irrational KeyError LabelNode LinSpace LineNumberNode LoadError LowerTriangular MIME Matrix MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode NullException Nullable Number ObjectIdDict OrdinalRange OutOfMemoryError OverflowError Pair ParseError PartialQuickSort PermutedDimsArray Pipe PollingFileWatcher ProcessExitedException Ptr QuoteNode RandomDevice Range RangeIndex Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RevString RoundingMode RowVector SSAValue SegmentationFault SerializationState Set SharedArray SharedMatrix SharedVector Signed SimpleVector Slot SlotNumber SparseMatrixCSC SparseVector StackFrame StackOverflowError StackTrace StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubString SymTridiagonal Symbol Symmetric SystemError TCPSocket Task Text TextDisplay Timer Tridiagonal Tuple Type TypeError TypeMapEntry TypeMapLevel TypeName TypeVar TypedSlot UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefRefError UndefVarError UnicodeError UniformScaling Union UnionAll UnitRange Unsigned UpperTriangular Val Vararg VecElement VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef WorkerConfig WorkerPool "},a={keywords:t,illegal:/<\//},n={className:"subst",begin:/\$\(/,end:/\)/,keywords:t},o={className:"variable",begin:"\\$"+r},i={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],variants:[{begin:/\w*"""/,end:/"""\w*/,relevance:10},{begin:/\w*"/,end:/"\w*/}]},l={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],begin:"`",end:"`"},s={className:"meta",begin:"@"+r};return a.name="Julia",a.contains=[{className:"number",begin:/(\b0x[\d_]*(\.[\d_]*)?|0x\.\d[\d_]*)p[-+]?\d+|\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\b\d[\d_]*(\.[\d_]*)?|\.\d[\d_]*)([eEfF][-+]?\d+)?/,relevance:0},{className:"string",begin:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},i,l,s,{className:"comment",variants:[{begin:"#=",end:"=#",relevance:10},{begin:"#",end:"$"}]},e.HASH_COMMENT_MODE,{className:"keyword",begin:"\\b(((abstract|primitive)\\s+)type|(mutable\\s+)?struct)\\b"},{begin:/<:/}],n.contains=a.contains,a}}());hljs.registerLanguage("php-template",function(){"use strict";return function(n){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}());hljs.registerLanguage("scss",function(){"use strict";return function(e){var t={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},i={className:"number",begin:"#[0-9A-Fa-f]+"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:"\\#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{className:"selector-tag",begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",relevance:0},{className:"selector-pseudo",begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{className:"selector-pseudo",begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},t,{className:"attribute",begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",illegal:"[^\\s]"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:":",end:";",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,keywords:"and or not only",contains:[{begin:"@[a-z-]+",className:"keyword"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}());hljs.registerLanguage("r",function(){"use strict";return function(e){var n="([a-zA-Z]|\\.[a-zA-Z.])[a-zA-Z0-9._]*";return{name:"R",contains:[e.HASH_COMMENT_MODE,{begin:n,keywords:{$pattern:n,keyword:"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...",literal:"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10"},relevance:0},{className:"number",begin:"0[xX][0-9a-fA-F]+[Li]?\\b",relevance:0},{className:"number",begin:"\\d+(?:[eE][+\\-]?\\d*)?L\\b",relevance:0},{className:"number",begin:"\\d+\\.(?!\\d)(?:i\\b)?",relevance:0},{className:"number",begin:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{className:"number",begin:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{begin:"`",end:"`",relevance:0},{className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:'"',end:'"'},{begin:"'",end:"'"}]}]}}}());hljs.registerLanguage("sql",function(){"use strict";return function(e){var t=e.COMMENT("--","$");return{name:"SQL",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with",end:/;/,endsWithParent:!0,keywords:{$pattern:/[\w\.]+/,keyword:"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null unknown",built_in:"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void"},contains:[{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:'"',end:'"',contains:[{begin:'""'}]},{className:"string",begin:"`",end:"`"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}());hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}());hljs.registerLanguage("json",function(){"use strict";return function(n){var e={literal:"true false null"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:",",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(a,{begin:/:/})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(a)],illegal:"\\S"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:"JSON",contains:t,keywords:e,illegal:"\\S"}}}());hljs.registerLanguage("python-repl",function(){"use strict";return function(n){return{aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}}());hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}());hljs.registerLanguage("javascript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return r("(?=",e,")")}function r(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(t){var i="[A-Za-z$_][0-9A-Za-z$_]*",c={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/},o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.join(" "),literal:n.join(" "),built_in:a.join(" ")},l={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:t.C_NUMBER_RE+"n?"}],relevance:0},E={className:"subst",begin:"\\$\\{",end:"\\}",keywords:o,contains:[]},d={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},g={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},u={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]};E.contains=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,l,t.REGEXP_MODE];var b=E.contains.concat([{begin:/\(/,end:/\)/,contains:["self"].concat(E.contains,[t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE])},t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]),_={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:b};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:o,contains:[t.SHEBANG({binary:"node",relevance:5}),{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,t.C_LINE_COMMENT_MODE,t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0},{className:"variable",begin:i+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,l,{begin:r(/[{,\n]\s*/,s(r(/(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/,i+"\\s*:"))),relevance:0,contains:[{className:"attr",begin:i+s("\\s*:"),relevance:0}]},{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,t.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:b}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:c.begin,end:c.end}],subLanguage:"xml",contains:[{begin:c.begin,end:c.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:i}),_],illegal:/\[|%/},{begin:/\$[(.]/},t.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0},{begin:"(get|set)\\s+(?="+i+"\\()",end:/{/,keywords:"get set",contains:[t.inherit(t.TITLE_MODE,{begin:i}),{begin:/\(\)/},_]}],illegal:/#(?!!)/}}}());hljs.registerLanguage("typescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "),literal:n.join(" "),built_in:a.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ")},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},i={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:r.C_NUMBER_RE+"n?"}],relevance:0},o={className:"subst",begin:"\\$\\{",end:"\\}",keywords:t,contains:[]},c={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"xml"}},l={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"css"}},E={className:"string",begin:"`",end:"`",contains:[r.BACKSLASH_ESCAPE,o]};o.contains=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,i,r.REGEXP_MODE];var d={begin:"\\(",end:/\)/,keywords:t,contains:["self",r.QUOTE_STRING_MODE,r.APOS_STRING_MODE,r.NUMBER_MODE]},u={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,s,d]};return{name:"TypeScript",aliases:["ts"],keywords:t,contains:[r.SHEBANG(),{className:"meta",begin:/^\s*['"]use strict['"]/},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,i,{begin:"("+r.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,r.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+r.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:r.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:d.contains}]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[\{;]/,excludeEnd:!0,keywords:t,contains:["self",r.inherit(r.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),u],illegal:/%/,relevance:0},{beginKeywords:"constructor",end:/[\{;]/,excludeEnd:!0,contains:["self",u]},{begin:/module\./,keywords:{built_in:"module"},relevance:0},{beginKeywords:"module",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:"interface extends"},{begin:/\$[(.]/},{begin:"\\."+r.IDENT_RE,relevance:0},s,d]}}}());hljs.registerLanguage("plaintext",function(){"use strict";return function(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}}());hljs.registerLanguage("less",function(){"use strict";return function(e){var n="([\\w-]+|@{[\\w-]+})",a=[],s=[],t=function(e){return{className:"string",begin:"~?"+e+".*?"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:"\\(",end:"\\)",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@{[\\w-]+}"),r("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},{className:"meta",begin:"!important"});var c=s.concat({begin:"{",end:"}",contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},o={begin:n+"\\s*:",returnBegin:!0,end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},g={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},d={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:"{"}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@{[\\w-]+}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"\\(",end:"\\)",contains:c},{begin:"!important"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}}());hljs.registerLanguage("lua",function(){"use strict";return function(e){var t={begin:"\\[=*\\[",end:"\\]=*\\]",contains:["self"]},a=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[","\\]=*\\]",{contains:[t],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:a.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:"\\[=*\\[",end:"\\]=*\\]",contains:[t],relevance:5}])}}}()); diff --git a/index.html b/index.html new file mode 100644 index 000000000000..b701c35d23d5 --- /dev/null +++ b/index.html @@ -0,0 +1,195 @@ + + + + + + Getting started - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Getting started

+

Kani is an open-source verification tool that uses model checking to analyze Rust programs. +Kani is particularly useful for verifying unsafe code blocks in Rust, where the "unsafe superpowers" are unchecked by the compiler. +Some example properties you can prove with Kani include memory safety properties (e.g., null pointer dereferences, use-after-free, etc.), the absence of certain runtime errors (i.e., index out of bounds, panics), and the absence of some types of unexpected behavior (e.g., arithmetic overflows). +Kani can also prove custom properties provided in the form of user-specified assertions. +As Kani uses model checking, Kani will either prove the property, disprove the +property (with a counterexample), or may run out of resources.

+

Kani uses proof harnesses to analyze programs. +Proof harnesses are similar to test harnesses, especially property-based test harnesses.

+

Project Status

+

Kani is currently under active development. +Releases are published here. +Major changes to Kani are documented in the RFC Book. +We also publish updates on Kani use cases and features on our blog.

+

There is support for a fair amount of Rust language features, but not all (e.g., concurrency). +Please see Limitations for a detailed list of supported features.

+

Kani releases every two weeks. +As part of every release, Kani will synchronize with a recent nightly release of Rust, and so is generally up-to-date with the latest Rust language features.

+

If you encounter issues when using Kani, we encourage you to report them to us.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/install-github-ci.html b/install-github-ci.html new file mode 100644 index 000000000000..0444afd07eb7 --- /dev/null +++ b/install-github-ci.html @@ -0,0 +1,237 @@ + + + + + + GitHub CI Action - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

GitHub Action

+

Kani offers a GitHub Action for running Kani in CI. +As of now, only Ubuntu 20.04 with x86_64-unknown-linux-gnu is supported for Kani in CI.

+

Using Kani in your GitHub workflow

+

Our GitHub Action is available in the GitHub Marketplace.

+

The following workflow snippet will checkout your repository and run cargo kani on it whenever a push or pull request occurs. +Replace <MAJOR>.<MINOR> with the version of Kani you want to run with.

+
name: Kani CI
+on:
+  pull_request:
+  push:
+jobs:
+  run-kani:
+    runs-on: ubuntu-20.04
+    steps:
+      - name: 'Checkout your code.'
+        uses: actions/checkout@v3
+
+      - name: 'Run Kani on your code.'
+        uses: model-checking/kani-github-action@v<MAJOR>.<MINOR>
+
+

This will run cargo kani on the code you checked out.

+

Options

+

The action takes the following optional parameters:

+
    +
  • command: The command to run. +Defaults to cargo kani. +Most often, you will not need to change this.
  • +
  • working-directory: The directory to execute the command in. +Defaults to .. +Useful if your repository has multiple crates, and you only want to run on one of them.
  • +
  • args: The arguments to pass to the given ${command}. +See cargo kani --help for a full list of options. +Useful options include: +
      +
    • --output-format=terse to generate terse output.
    • +
    • --tests to run on proofs inside the test module (needed for running Bolero).
    • +
    • --workspace to run on all crates within your repository.
    • +
    +
  • +
+

FAQ

+
    +
  • Kani takes too long for my CI: Try running Kani on a +schedule +with desired frequency.
  • +
  • Kani Silently Crashes with no logs: Few possible reasons: +
      +
    • Kani ran out of RAM. GitHub offers up to 7GB of RAM, but Kani may +use more. Run locally to confirm.
    • +
    • GitHub terminates jobs longer than 6 hours.
    • +
    • Otherwise, consider filing an issue here.
    • +
    +
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/install-guide.html b/install-guide.html new file mode 100644 index 000000000000..e0b877c29cf3 --- /dev/null +++ b/install-guide.html @@ -0,0 +1,237 @@ + + + + + + Installation - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Installation

+

Kani offers an easy installation option on three platforms:

+
    +
  • x86_64-unknown-linux-gnu (Most Linux distributions)
  • +
  • x86_64-apple-darwin (Intel Mac OS)
  • +
  • aarch64-apple-darwin (Apple Silicon Mac OS)
  • +
+

Other platforms are either not yet supported or require instead that +you build from source. To use Kani in your +GitHub CI workflows, see GitHub CI Action.

+

Dependencies

+

The following must already be installed:

+
    +
  • Python version 3.7 or newer and the package installer pip.
  • +
  • Rust 1.58 or newer installed via rustup.
  • +
+

Installing the latest version

+

To install the latest version of Kani, run:

+
cargo install --locked kani-verifier
+cargo kani setup
+
+

This will build and place in ~/.cargo/bin (in a typical environment) the kani and cargo-kani binaries. +The second step (cargo kani setup) will download the Kani compiler and other necessary dependencies, and place them under ~/.kani/ by default. +A custom path can be specified using the KANI_HOME environment variable.

+

Installing an older version

+
cargo install --locked kani-verifier --version <VERSION>
+cargo kani setup
+
+

Checking your installation

+

After you've installed Kani, +you can try running it by creating a test file:

+
// File: test.rs
+#[kani::proof]
+fn main() {
+    assert!(1 == 2);
+}
+
+

Run Kani on the single file:

+
kani test.rs
+
+

You should get a result like this one:

+
[...]
+RESULTS:
+Check 1: main.assertion.1
+         - Status: FAILURE
+         - Description: "assertion failed: 1 == 2"
+[...]
+VERIFICATION:- FAILED
+
+

Fix the test and you should see a result like this one:

+
[...]
+VERIFICATION:- SUCCESSFUL
+
+

Next steps

+

If you're learning Kani for the first time, you may be interested in our tutorial.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/kani-tutorial.html b/kani-tutorial.html new file mode 100644 index 000000000000..ce3c04cbc4e4 --- /dev/null +++ b/kani-tutorial.html @@ -0,0 +1,191 @@ + + + + + + Tutorial - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Tutorial

+
+

NOTE: This tutorial expects you to have followed the Kani installation instructions first.

+
+

This tutorial will step you through a progression from simple to moderately complex tasks with Kani. +It's meant to ensure you can get started, and see at least some simple examples of how typical proofs are structured. +It will also teach you the basics of "debugging" proof harnesses, which mainly consists of diagnosing and resolving non-termination issues with the solver.

+

You may also want to read the Application section to better +understand what Kani is and how it can be applied on real code.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/limitations.html b/limitations.html new file mode 100644 index 000000000000..f58d3cba4767 --- /dev/null +++ b/limitations.html @@ -0,0 +1,195 @@ + + + + + + Limitations - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Limitations

+

Like other tools, Kani comes with some limitations. In some cases, these +limitations are inherent because of the techniques it's based on, or the +undecidability of the properties that Kani seeks to prove. In other +cases, it's just a matter of time and effort to remove these limitations (e.g., +specific unsupported Rust language features).

+

In this chapter, we do the following to document these limitations:

+ + +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/mark.min.js b/mark.min.js new file mode 100644 index 000000000000..163623188347 --- /dev/null +++ b/mark.min.js @@ -0,0 +1,7 @@ +/*!*************************************************** +* mark.js v8.11.1 +* https://markjs.io/ +* Copyright (c) 2014–2018, Julian Kühnel +* Released under the MIT license https://git.io/vwTVl +*****************************************************/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Mark=t()}(this,function(){"use strict";var e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:5e3;t(this,e),this.ctx=n,this.iframes=r,this.exclude=i,this.iframesTimeout=o}return n(e,[{key:"getContexts",value:function(){var e=[];return(void 0!==this.ctx&&this.ctx?NodeList.prototype.isPrototypeOf(this.ctx)?Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?this.ctx:"string"==typeof this.ctx?Array.prototype.slice.call(document.querySelectorAll(this.ctx)):[this.ctx]:[]).forEach(function(t){var n=e.filter(function(e){return e.contains(t)}).length>0;-1!==e.indexOf(t)||n||e.push(t)}),e}},{key:"getIframeContents",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){},r=void 0;try{var i=e.contentWindow;if(r=i.document,!i||!r)throw new Error("iframe inaccessible")}catch(e){n()}r&&t(r)}},{key:"isIframeBlank",value:function(e){var t="about:blank",n=e.getAttribute("src").trim();return e.contentWindow.location.href===t&&n!==t&&n}},{key:"observeIframeLoad",value:function(e,t,n){var r=this,i=!1,o=null,a=function a(){if(!i){i=!0,clearTimeout(o);try{r.isIframeBlank(e)||(e.removeEventListener("load",a),r.getIframeContents(e,t,n))}catch(e){n()}}};e.addEventListener("load",a),o=setTimeout(a,this.iframesTimeout)}},{key:"onIframeReady",value:function(e,t,n){try{"complete"===e.contentWindow.document.readyState?this.isIframeBlank(e)?this.observeIframeLoad(e,t,n):this.getIframeContents(e,t,n):this.observeIframeLoad(e,t,n)}catch(e){n()}}},{key:"waitForIframes",value:function(e,t){var n=this,r=0;this.forEachIframe(e,function(){return!0},function(e){r++,n.waitForIframes(e.querySelector("html"),function(){--r||t()})},function(e){e||t()})}},{key:"forEachIframe",value:function(t,n,r){var i=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},a=t.querySelectorAll("iframe"),s=a.length,c=0;a=Array.prototype.slice.call(a);var u=function(){--s<=0&&o(c)};s||u(),a.forEach(function(t){e.matches(t,i.exclude)?u():i.onIframeReady(t,function(e){n(t)&&(c++,r(e)),u()},u)})}},{key:"createIterator",value:function(e,t,n){return document.createNodeIterator(e,t,n,!1)}},{key:"createInstanceOnIframe",value:function(t){return new e(t.querySelector("html"),this.iframes)}},{key:"compareNodeIframe",value:function(e,t,n){if(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_PRECEDING){if(null===t)return!0;if(t.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)return!0}return!1}},{key:"getIteratorNode",value:function(e){var t=e.previousNode();return{prevNode:t,node:null===t?e.nextNode():e.nextNode()&&e.nextNode()}}},{key:"checkIframeFilter",value:function(e,t,n,r){var i=!1,o=!1;return r.forEach(function(e,t){e.val===n&&(i=t,o=e.handled)}),this.compareNodeIframe(e,t,n)?(!1!==i||o?!1===i||o||(r[i].handled=!0):r.push({val:n,handled:!0}),!0):(!1===i&&r.push({val:n,handled:!1}),!1)}},{key:"handleOpenIframes",value:function(e,t,n,r){var i=this;e.forEach(function(e){e.handled||i.getIframeContents(e.val,function(e){i.createInstanceOnIframe(e).forEachNode(t,n,r)})})}},{key:"iterateThroughNodes",value:function(e,t,n,r,i){for(var o,a=this,s=this.createIterator(t,e,r),c=[],u=[],l=void 0,h=void 0;void 0,o=a.getIteratorNode(s),h=o.prevNode,l=o.node;)this.iframes&&this.forEachIframe(t,function(e){return a.checkIframeFilter(l,h,e,c)},function(t){a.createInstanceOnIframe(t).forEachNode(e,function(e){return u.push(e)},r)}),u.push(l);u.forEach(function(e){n(e)}),this.iframes&&this.handleOpenIframes(c,e,n,r),i()}},{key:"forEachNode",value:function(e,t,n){var r=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},o=this.getContexts(),a=o.length;a||i(),o.forEach(function(o){var s=function(){r.iterateThroughNodes(e,o,t,n,function(){--a<=0&&i()})};r.iframes?r.waitForIframes(o,s):s()})}}],[{key:"matches",value:function(e,t){var n="string"==typeof t?[t]:t,r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector;if(r){var i=!1;return n.every(function(t){return!r.call(e,t)||(i=!0,!1)}),i}return!1}}]),e}(),o=function(){function e(n){t(this,e),this.opt=r({},{diacritics:!0,synonyms:{},accuracy:"partially",caseSensitive:!1,ignoreJoiners:!1,ignorePunctuation:[],wildcards:"disabled"},n)}return n(e,[{key:"create",value:function(e){return"disabled"!==this.opt.wildcards&&(e=this.setupWildcardsRegExp(e)),e=this.escapeStr(e),Object.keys(this.opt.synonyms).length&&(e=this.createSynonymsRegExp(e)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),this.opt.diacritics&&(e=this.createDiacriticsRegExp(e)),e=this.createMergedBlanksRegExp(e),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.createJoinersRegExp(e)),"disabled"!==this.opt.wildcards&&(e=this.createWildcardsRegExp(e)),e=this.createAccuracyRegExp(e),new RegExp(e,"gm"+(this.opt.caseSensitive?"":"i"))}},{key:"escapeStr",value:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}},{key:"createSynonymsRegExp",value:function(e){var t=this.opt.synonyms,n=this.opt.caseSensitive?"":"i",r=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(var i in t)if(t.hasOwnProperty(i)){var o=t[i],a="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(i):this.escapeStr(i),s="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(o):this.escapeStr(o);""!==a&&""!==s&&(e=e.replace(new RegExp("("+this.escapeStr(a)+"|"+this.escapeStr(s)+")","gm"+n),r+"("+this.processSynonyms(a)+"|"+this.processSynonyms(s)+")"+r))}return e}},{key:"processSynonyms",value:function(e){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),e}},{key:"setupWildcardsRegExp",value:function(e){return(e=e.replace(/(?:\\)*\?/g,function(e){return"\\"===e.charAt(0)?"?":""})).replace(/(?:\\)*\*/g,function(e){return"\\"===e.charAt(0)?"*":""})}},{key:"createWildcardsRegExp",value:function(e){var t="withSpaces"===this.opt.wildcards;return e.replace(/\u0001/g,t?"[\\S\\s]?":"\\S?").replace(/\u0002/g,t?"[\\S\\s]*?":"\\S*")}},{key:"setupIgnoreJoinersRegExp",value:function(e){return e.replace(/[^(|)\\]/g,function(e,t,n){var r=n.charAt(t+1);return/[(|)\\]/.test(r)||""===r?e:e+"\0"})}},{key:"createJoinersRegExp",value:function(e){var t=[],n=this.opt.ignorePunctuation;return Array.isArray(n)&&n.length&&t.push(this.escapeStr(n.join(""))),this.opt.ignoreJoiners&&t.push("\\u00ad\\u200b\\u200c\\u200d"),t.length?e.split(/\u0000+/).join("["+t.join("")+"]*"):e}},{key:"createDiacriticsRegExp",value:function(e){var t=this.opt.caseSensitive?"":"i",n=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"],r=[];return e.split("").forEach(function(i){n.every(function(n){if(-1!==n.indexOf(i)){if(r.indexOf(n)>-1)return!1;e=e.replace(new RegExp("["+n+"]","gm"+t),"["+n+"]"),r.push(n)}return!0})}),e}},{key:"createMergedBlanksRegExp",value:function(e){return e.replace(/[\s]+/gim,"[\\s]+")}},{key:"createAccuracyRegExp",value:function(e){var t=this,n=this.opt.accuracy,r="string"==typeof n?n:n.value,i="";switch(("string"==typeof n?[]:n.limiters).forEach(function(e){i+="|"+t.escapeStr(e)}),r){case"partially":default:return"()("+e+")";case"complementary":return"()([^"+(i="\\s"+(i||this.escapeStr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿")))+"]*"+e+"[^"+i+"]*)";case"exactly":return"(^|\\s"+i+")("+e+")(?=$|\\s"+i+")"}}}]),e}(),a=function(){function a(e){t(this,a),this.ctx=e,this.ie=!1;var n=window.navigator.userAgent;(n.indexOf("MSIE")>-1||n.indexOf("Trident")>-1)&&(this.ie=!0)}return n(a,[{key:"log",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"debug",r=this.opt.log;this.opt.debug&&"object"===(void 0===r?"undefined":e(r))&&"function"==typeof r[n]&&r[n]("mark.js: "+t)}},{key:"getSeparatedKeywords",value:function(e){var t=this,n=[];return e.forEach(function(e){t.opt.separateWordSearch?e.split(" ").forEach(function(e){e.trim()&&-1===n.indexOf(e)&&n.push(e)}):e.trim()&&-1===n.indexOf(e)&&n.push(e)}),{keywords:n.sort(function(e,t){return t.length-e.length}),length:n.length}}},{key:"isNumeric",value:function(e){return Number(parseFloat(e))==e}},{key:"checkRanges",value:function(e){var t=this;if(!Array.isArray(e)||"[object Object]"!==Object.prototype.toString.call(e[0]))return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(e),[];var n=[],r=0;return e.sort(function(e,t){return e.start-t.start}).forEach(function(e){var i=t.callNoMatchOnInvalidRanges(e,r),o=i.start,a=i.end;i.valid&&(e.start=o,e.length=a-o,n.push(e),r=a)}),n}},{key:"callNoMatchOnInvalidRanges",value:function(e,t){var n=void 0,r=void 0,i=!1;return e&&void 0!==e.start?(r=(n=parseInt(e.start,10))+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&r-t>0&&r-n>0?i=!0:(this.log("Ignoring invalid or overlapping range: "+JSON.stringify(e)),this.opt.noMatch(e))):(this.log("Ignoring invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:n,end:r,valid:i}}},{key:"checkWhitespaceRanges",value:function(e,t,n){var r=void 0,i=!0,o=n.length,a=t-o,s=parseInt(e.start,10)-a;return(r=(s=s>o?o:s)+parseInt(e.length,10))>o&&(r=o,this.log("End range automatically set to the max value of "+o)),s<0||r-s<0||s>o||r>o?(i=!1,this.log("Invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)):""===n.substring(s,r).replace(/\s+/g,"")&&(i=!1,this.log("Skipping whitespace only range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:s,end:r,valid:i}}},{key:"getTextNodes",value:function(e){var t=this,n="",r=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,function(e){r.push({start:n.length,end:(n+=e.textContent).length,node:e})},function(e){return t.matchesExclude(e.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},function(){e({value:n,nodes:r})})}},{key:"matchesExclude",value:function(e){return i.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}},{key:"wrapRangeInTextNode",value:function(e,t,n){var r=this.opt.element?this.opt.element:"mark",i=e.splitText(t),o=i.splitText(n-t),a=document.createElement(r);return a.setAttribute("data-markjs","true"),this.opt.className&&a.setAttribute("class",this.opt.className),a.textContent=i.textContent,i.parentNode.replaceChild(a,i),o}},{key:"wrapRangeInMappedTextNode",value:function(e,t,n,r,i){var o=this;e.nodes.every(function(a,s){var c=e.nodes[s+1];if(void 0===c||c.start>t){if(!r(a.node))return!1;var u=t-a.start,l=(n>a.end?a.end:n)-a.start,h=e.value.substr(0,a.start),f=e.value.substr(l+a.start);if(a.node=o.wrapRangeInTextNode(a.node,u,l),e.value=h+f,e.nodes.forEach(function(t,n){n>=s&&(e.nodes[n].start>0&&n!==s&&(e.nodes[n].start-=l),e.nodes[n].end-=l)}),n-=l,i(a.node.previousSibling,a.start),!(n>a.end))return!1;t=a.end}return!0})}},{key:"wrapGroups",value:function(e,t,n,r){return r((e=this.wrapRangeInTextNode(e,t,t+n)).previousSibling),e}},{key:"separateGroups",value:function(e,t,n,r,i){for(var o=t.length,a=1;a-1&&r(t[a],e)&&(e=this.wrapGroups(e,s,t[a].length,i))}return e}},{key:"wrapMatches",value:function(e,t,n,r,i){var o=this,a=0===t?0:t+1;this.getTextNodes(function(t){t.nodes.forEach(function(t){t=t.node;for(var i=void 0;null!==(i=e.exec(t.textContent))&&""!==i[a];){if(o.opt.separateGroups)t=o.separateGroups(t,i,a,n,r);else{if(!n(i[a],t))continue;var s=i.index;if(0!==a)for(var c=1;c + + + + + Overrides - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Overrides

+

As explained in Comparison with other +tools, Kani is based on a +technique called model checking, which verifies a program without actually +executing it. It does so through encoding the program and analyzing the encoded +version. The encoding process often requires "modeling" some of the library +functions to make them suitable for analysis. Typical examples of functionality +that requires modeling are system calls and I/O operations. In some cases, Kani +performs such encoding through overriding some of the definitions in the Rust +standard library.

+

The following table lists some of the symbols that Kani +overrides and a description of their behavior compared to the std versions:

+ + + + + + +
NameDescription
assert, assert_eq, and assert_ne macrosSkips string formatting code, generates a more informative message and performs some instrumentation
debug_assert, debug_assert_eq, and debug_assert_ne macrosRewrites as equivalent assert* macro
print, eprint, println, and eprintln macrosSkips string formatting and I/O operations
unreachable macroSkips string formatting and invokes panic!()
std::process::{abort, exit} functionsInvokes panic!() to abort the execution
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/performance-comparisons.html b/performance-comparisons.html new file mode 100644 index 000000000000..30f39b367571 --- /dev/null +++ b/performance-comparisons.html @@ -0,0 +1,214 @@ + + + + + + Performance comparisons - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Performance comparisons with benchcomp

+

While Kani includes a performance regression suite, you may wish to test Kani's performance using your own benchmarks or with particular versions of Kani. +You can use the benchcomp tool in the Kani repository to run several 'variants' of a command on one or more benchmark suites; automatically parse the results of each of those suites; and take actions or emit visualizations based on those results.

+

Example use-cases

+
    +
  1. Run one or more benchmark suites with the current and previous versions of Kani. +Exit with a return code of 1 or print a custom summary to the terminal if any benchmark regressed by more than a user-configured amount.
  2. +
  3. Run benchmark suites using several historical versions of Kani and emit a graph of performance over time.
  4. +
  5. Run benchmark suites using different SAT solvers, command-line flags, or environment variables.
  6. +
+

Features

+

Benchcomp provides the following features to support your performance-comparison workflow:

+
    +
  • Automatically copies benchmark suites into a fresh directories before running with each variant, to ensure that built artifacts do not affect subsequent runtimes
  • +
  • Parses the results of different 'kinds' of benchmark suite and combines those results into a single unified format. +This allows you to run benchmarks from external repositories, suites of pre-compiled GOTO-binaries, and other kinds of benchmark all together and view their results in a single dashboard.
  • +
  • Driven by a single configuration file that can be sent to colleagues or checked into a repository to be used in continuous integration.
  • +
  • Extensible, allowing you to write your own parsers and visualizations.
  • +
  • Caches all previous runs and allows you to re-create visualizations for the latest run without actually re-running the suites.
  • +
+

Quick start

+

Here's how to run Kani's performance suite twice, comparing the last released version of Kani with the current HEAD.

+
cd $KANI_SRC_DIR
+git worktree add new HEAD
+git worktree add old $(git describe --tags --abbrev=0)
+
+tools/benchcomp/bin/benchcomp --config tools/benchcomp/configs/perf-regression.yaml
+
+

This uses the perf-regression.yaml configuration file that we use in continuous integration. +After running the suite twice, the configuration file terminates benchcomp with a return code of 1 if any of the benchmarks regressed on metrics such as success (a boolean), solver_runtime, and number_vccs (numerical). +Additionally, the config file directs benchcomp to print out a Markdown table that GitHub's CI summary page renders in to a table.

+

The rest of this documentation describes how to modify benchcomp for your own use cases, including writing a configuration file; writing a custom parser for your benchmark suite; and writing a custom visualization to examine the results of a performance comparison.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/print.html b/print.html new file mode 100644 index 000000000000..7c75b7f87de7 --- /dev/null +++ b/print.html @@ -0,0 +1,4050 @@ + + + + + + The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Getting started

+

Kani is an open-source verification tool that uses model checking to analyze Rust programs. +Kani is particularly useful for verifying unsafe code blocks in Rust, where the "unsafe superpowers" are unchecked by the compiler. +Some example properties you can prove with Kani include memory safety properties (e.g., null pointer dereferences, use-after-free, etc.), the absence of certain runtime errors (i.e., index out of bounds, panics), and the absence of some types of unexpected behavior (e.g., arithmetic overflows). +Kani can also prove custom properties provided in the form of user-specified assertions. +As Kani uses model checking, Kani will either prove the property, disprove the +property (with a counterexample), or may run out of resources.

+

Kani uses proof harnesses to analyze programs. +Proof harnesses are similar to test harnesses, especially property-based test harnesses.

+

Project Status

+

Kani is currently under active development. +Releases are published here. +Major changes to Kani are documented in the RFC Book. +We also publish updates on Kani use cases and features on our blog.

+

There is support for a fair amount of Rust language features, but not all (e.g., concurrency). +Please see Limitations for a detailed list of supported features.

+

Kani releases every two weeks. +As part of every release, Kani will synchronize with a recent nightly release of Rust, and so is generally up-to-date with the latest Rust language features.

+

If you encounter issues when using Kani, we encourage you to report them to us.

+

Installation

+

Kani offers an easy installation option on three platforms:

+
    +
  • x86_64-unknown-linux-gnu (Most Linux distributions)
  • +
  • x86_64-apple-darwin (Intel Mac OS)
  • +
  • aarch64-apple-darwin (Apple Silicon Mac OS)
  • +
+

Other platforms are either not yet supported or require instead that +you build from source. To use Kani in your +GitHub CI workflows, see GitHub CI Action.

+

Dependencies

+

The following must already be installed:

+
    +
  • Python version 3.7 or newer and the package installer pip.
  • +
  • Rust 1.58 or newer installed via rustup.
  • +
+

Installing the latest version

+

To install the latest version of Kani, run:

+
cargo install --locked kani-verifier
+cargo kani setup
+
+

This will build and place in ~/.cargo/bin (in a typical environment) the kani and cargo-kani binaries. +The second step (cargo kani setup) will download the Kani compiler and other necessary dependencies, and place them under ~/.kani/ by default. +A custom path can be specified using the KANI_HOME environment variable.

+

Installing an older version

+
cargo install --locked kani-verifier --version <VERSION>
+cargo kani setup
+
+

Checking your installation

+

After you've installed Kani, +you can try running it by creating a test file:

+
// File: test.rs
+#[kani::proof]
+fn main() {
+    assert!(1 == 2);
+}
+
+

Run Kani on the single file:

+
kani test.rs
+
+

You should get a result like this one:

+
[...]
+RESULTS:
+Check 1: main.assertion.1
+         - Status: FAILURE
+         - Description: "assertion failed: 1 == 2"
+[...]
+VERIFICATION:- FAILED
+
+

Fix the test and you should see a result like this one:

+
[...]
+VERIFICATION:- SUCCESSFUL
+
+

Next steps

+

If you're learning Kani for the first time, you may be interested in our tutorial.

+

Installing from source code

+
+

If you were able to install Kani normally, you do not need to build Kani from source. +You probably want to proceed to the Kani tutorial.

+
+

Dependencies

+

In general, the following dependencies are required to build Kani from source.

+
+

NOTE: These dependencies may be installed by running the scripts shown +below and don't need to be manually installed.

+
+
    +
  1. Cargo installed via rustup
  2. +
  3. CBMC (latest release)
  4. +
  5. Kissat (Release 4.0.1)
  6. +
+

Kani has been tested in Ubuntu and macOS platforms.

+

Install dependencies on Ubuntu

+

Support is available for Ubuntu 20.04, 22.04, and 24.04. +The simplest way to install dependencies (especially if you're using a fresh VM) +is following our CI scripts:

+
# git clone git@github.com:model-checking/kani.git
+git clone https://github.com/model-checking/kani.git
+cd kani
+git submodule update --init
+./scripts/setup/ubuntu/install_deps.sh
+# If you haven't already (or from https://rustup.rs/):
+./scripts/setup/install_rustup.sh
+source $HOME/.cargo/env
+
+

Install dependencies on macOS

+

Support is available for macOS 11. You need to have Homebrew installed already.

+
# git clone git@github.com:model-checking/kani.git
+git clone https://github.com/model-checking/kani.git
+cd kani
+git submodule update --init
+./scripts/setup/macos/install_deps.sh
+# If you haven't already (or from https://rustup.rs/):
+./scripts/setup/install_rustup.sh
+source $HOME/.cargo/env
+
+

Build and test Kani

+

Build the Kani package using:

+
cargo build-dev -- --release
+
+

to compile with optimizations turned on or using:

+
cargo build-dev
+
+

to compile in debug/development mode.

+

Then, optionally, run the regression tests:

+
./scripts/kani-regression.sh
+
+

This script has a lot of noisy output, but on a successful run you'll see at the end of the execution:

+
All Kani regression tests completed successfully.
+
+

Adding Kani to your path

+

To use a locally-built Kani from anywhere, add the Kani scripts to your path:

+
export PATH=$(pwd)/scripts:$PATH
+
+

Next steps

+

If you're learning Kani for the first time, you may be interested in our tutorial.

+

GitHub Action

+

Kani offers a GitHub Action for running Kani in CI. +As of now, only Ubuntu 20.04 with x86_64-unknown-linux-gnu is supported for Kani in CI.

+

Using Kani in your GitHub workflow

+

Our GitHub Action is available in the GitHub Marketplace.

+

The following workflow snippet will checkout your repository and run cargo kani on it whenever a push or pull request occurs. +Replace <MAJOR>.<MINOR> with the version of Kani you want to run with.

+
name: Kani CI
+on:
+  pull_request:
+  push:
+jobs:
+  run-kani:
+    runs-on: ubuntu-20.04
+    steps:
+      - name: 'Checkout your code.'
+        uses: actions/checkout@v3
+
+      - name: 'Run Kani on your code.'
+        uses: model-checking/kani-github-action@v<MAJOR>.<MINOR>
+
+

This will run cargo kani on the code you checked out.

+

Options

+

The action takes the following optional parameters:

+
    +
  • command: The command to run. +Defaults to cargo kani. +Most often, you will not need to change this.
  • +
  • working-directory: The directory to execute the command in. +Defaults to .. +Useful if your repository has multiple crates, and you only want to run on one of them.
  • +
  • args: The arguments to pass to the given ${command}. +See cargo kani --help for a full list of options. +Useful options include: +
      +
    • --output-format=terse to generate terse output.
    • +
    • --tests to run on proofs inside the test module (needed for running Bolero).
    • +
    • --workspace to run on all crates within your repository.
    • +
    +
  • +
+

FAQ

+
    +
  • Kani takes too long for my CI: Try running Kani on a +schedule +with desired frequency.
  • +
  • Kani Silently Crashes with no logs: Few possible reasons: +
      +
    • Kani ran out of RAM. GitHub offers up to 7GB of RAM, but Kani may +use more. Run locally to confirm.
    • +
    • GitHub terminates jobs longer than 6 hours.
    • +
    • Otherwise, consider filing an issue here.
    • +
    +
  • +
+

Using Kani

+

At present, Kani can used in two ways:

+ +

If you plan to integrate Kani in your projects, the recommended approach is to use cargo kani. +If you're already using cargo, this will handle dependencies automatically, and it can be configured (if needed) in Cargo.toml. +But kani is useful for small examples/tests.

+

Usage on a package

+

Kani is integrated with cargo and can be invoked from a package as follows:

+
cargo kani [OPTIONS]
+
+

This works like cargo test except that it will analyze all proof harnesses instead of running all test harnesses.

+

Common command line flags

+

Common to both kani and cargo kani are many command-line flags:

+
    +
  • +

    --concrete-playback=[print|inplace]: Experimental, --enable-unstable feature that generates a Rust unit test case +that plays back a failing proof harness using a concrete counterexample. +If used with print, Kani will only print the unit test to stdout. +If used with inplace, Kani will automatically add the unit test to the user's source code, next to the proof harness. For more detailed instructions, see the concrete playback section.

    +
  • +
  • +

    --tests: Build in "test mode", i.e. with cfg(test) set and dev-dependencies available (when using cargo kani).

    +
  • +
  • +

    --harness <name>: By default, Kani checks all proof harnesses it finds. +You can switch to checking a single harness using this flag.

    +
  • +
  • +

    --default-unwind <n>: Set a default global upper loop unwinding bound for proof harnesses. +This can force termination when CBMC tries to unwind loops indefinitely.

    +
  • +
+

Run cargo kani --help to see a complete list of arguments.

+

Usage on a single crate

+

For small examples or initial learning, it's very common to run Kani on just one source file. +The command line format for invoking Kani directly is the following:

+
kani filename.rs [OPTIONS]
+
+

This will build filename.rs and run all proof harnesses found within.

+

Configuration in Cargo.toml

+

Users can add a default configuration to the Cargo.toml file for running harnesses in a package. +Kani will extract any arguments from these sections:

+
    +
  • [workspace.metadata.kani.flags]
  • +
  • [package.metadata.kani.flags]
  • +
+

For example, if you want to set a default loop unwinding bound (when it's not otherwise specified), you can achieve this by adding the following lines to the package's Cargo.toml:

+
[package.metadata.kani.flags]
+default-unwind = 1
+
+

The options here are the same as on the command line (cargo kani --help), and flags (that is, command line arguments that don't take a value) are enabled by setting them to true.

+

Starting with Rust 1.80 (or nightly-2024-05-05), every reachable #[cfg] will be automatically checked that they match the expected config names and values. +To avoid warnings on cfg(kani), we recommend adding the check-cfg lint config in your crate's Cargo.toml as follows:

+
[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] }
+
+

For more information please consult this blog post.

+

The build process

+

When Kani builds your code, it does three important things:

+
    +
  1. It sets cfg(kani) for target crate compilation (including dependencies).
  2. +
  3. It injects the kani crate.
  4. +
  5. It sets cfg(kani_host) for host build targets such as any build script and procedural macro crates.
  6. +
+

A proof harness (which you can learn more about in the tutorial), is a function annotated with #[kani::proof] much like a test is annotated with #[test]. +But you may experience a similar problem using Kani as you would with dev-dependencies: if you try writing #[kani::proof] directly in your code, cargo build will fail because it doesn't know what the kani crate is.

+

This is why we recommend the same conventions as are used when writing tests in Rust: wrap your proof harnesses in cfg(kani) conditional compilation:

+
#[cfg(kani)]
+mod verification {
+    use super::*;
+
+    #[kani::proof]
+    pub fn check_something() {
+        // ....
+    }
+}
+
+

This will ensure that a normal build of your code will be completely unaffected by anything Kani-related.

+

This conditional compilation with cfg(kani) (as seen above) is still required for Kani proofs placed under tests/. +When this code is built by cargo test, the kani crate is not available, and so it would otherwise cause build failures. +(Whereas the use of dev-dependencies under tests/ does not need to be gated with cfg(test) since that code is already only built when testing.)

+

Verification results

+

Running Kani on a harness produces an output that includes a set of checks as +follows:

+
RESULTS:
+Check 1: example.assertion.1
+         - Status: <status>
+         - Description: <description>
+         - Location: <location>
+[...]
+
+

Kani determines the verification result for the harness based on the +result (i.e., <status>) of each individual check (also known as "properties"). If all +checks are successful then the overall verification result of the harness is successful. Otherwise the +verification fails, which indicates issues with the code under verification.

+

Check results

+

The result (or Status) of a check in Kani can be one of the following:

+
    +
  1. SUCCESS: This indicates that the check passed (i.e., the property holds). +Note that in some cases, the property may hold vacuously. This can occur +because the property is unreachable, or because the harness is +over-constrained.
  2. +
+

Example:

+
fn success_example() {
+    let mut sum = 0;
+    for i in 1..4 {
+        sum += i;
+    }
+    assert_eq!(sum, 6);
+}
+
+

The output from Kani indicates that the assertion holds:

+
Check 4: success_example.assertion.4
+         - Status: SUCCESS
+         - Description: "assertion failed: sum == 6"
+
+
    +
  1. FAILURE: This indicates that the check failed (i.e., the property doesn't +hold). In this case, please see the concrete playback +section for more help.
  2. +
+

Example:

+
fn failure_example() {
+    let arr = [1, 2, 3];
+    assert_ne!(arr.len(), 3);
+}
+
+

The assertion doesn't hold as Kani's output indicates:

+
Check 2: failure_example.assertion.2
+         - Status: FAILURE
+         - Description: "assertion failed: arr.len() != 3"
+
+
    +
  1. UNREACHABLE: This indicates that the check is unreachable (i.e., the +property holds vacuously). This occurs when there is no possible execution +trace that can reach the check's line of code. +This may be because the function that contains the check is unused, or because +the harness does not trigger the condition under which the check is invoked. +Kani currently checks reachability for the following assertion types: +
      +
    1. Assert macros (e.g. assert, assert_eq, etc.)
    2. +
    3. Arithmetic overflow checks
    4. +
    5. Negation overflow checks
    6. +
    7. Index out-of-bounds checks
    8. +
    9. Divide/remainder-by-zero checks
    10. +
    +
  2. +
+

Example:

+
fn unreachable_example() {
+    let x = 5;
+    let y = x + 2;
+    if x > y {
+        assert!(x < 8);
+    }
+}
+
+

The output from Kani indicates that the assertion is unreachable:

+
Check 2: unreachable_example.assertion.2
+         - Status: UNREACHABLE
+         - Description: "assertion failed: x < 8"
+
+
    +
  1. UNDETERMINED: This indicates that Kani was not able to conclude whether the +property holds or not. This can occur when the Rust program contains a construct +that is not currently supported by Kani. See +Rust feature support for Kani's current support of the +Rust language features.
  2. +
+

Example:

+
fn undetermined_example() {
+    let mut x = 0;
+    unsupp(&mut x);
+    assert!(x == 0);
+}
+
+#[feature(asm)]
+fn unsupp(x: &mut u8) {
+    unsafe {
+        std::arch::asm!("nop");
+    }
+}
+
+
+

The output from Kani indicates that the assertion is undetermined due to the +missing support for inline assembly in Kani:

+
Check 2: undetermined_example.assertion.2
+         - Status: UNDETERMINED
+         - Description: "assertion failed: x == 0"
+
+

Cover property results

+

Kani provides a kani::cover macro that can be used for checking whether a condition may occur at a certain point in the code.

+

The result of a cover property can be one of the following:

+
    +
  1. SATISFIED: This indicates that Kani found an execution that triggers the specified condition.
  2. +
+

The following example uses kani::cover to check if it's possible for x and y to hold the values 24 and 72, respectively, after 3 iterations of the while loop, which turns out to be the case.

+
#[kani::unwind(256)]
+fn cover_satisfied_example() {
+    let mut x: u8 = kani::any();
+    let mut y: u8 = kani::any();
+    y /= 2;
+    let mut i = 0;
+    while x != 0 && y != 0 {
+        kani::cover!(i > 2 && x == 24 && y == 72);
+        if x >= y { x -= y; }
+        else { y -= x; }
+        i += 1;
+    }
+}
+
+

Results:

+
Check 1: cover_satisfied_example.cover.1
+         - Status: SATISFIED
+         - Description: "cover condition: i > 2 && x == 24 && y == 72"
+         - Location: src/main.rs:60:9 in function cover_satisfied_example
+
+
    +
  1. UNSATISFIABLE: This indicates that Kani proved that the specified condition is impossible.
  2. +
+

The following example uses kani::cover to check if it's possible to have a UTF-8 encoded string consisting of 5 bytes that correspond to a string with a single character.

+
#[kani::unwind(6)]
+fn cover_unsatisfiable_example() {
+    let bytes: [u8; 5] = kani::any();
+    let s = std::str::from_utf8(&bytes);
+    if let Ok(s) = s {
+        kani::cover!(s.chars().count() <= 1);
+    }
+}
+
+

which is not possible as such string will contain at least two characters.

+
Check 46: cover_unsatisfiable_example.cover.1
+         - Status: UNSATISFIABLE
+         - Description: "cover condition: s.chars().count() <= 1"
+         - Location: src/main.rs:75:9 in function cover_unsatisfiable_example
+
+
    +
  1. UNREACHABLE: This indicates that the cover property itself is unreachable (i.e. it is vacuously unsatisfiable).
  2. +
+

In contrast to an UNREACHABLE result for assertions, an unreachable (or an unsatisfiable) cover property may indicate an incomplete proof.

+

Example: +In this example, a kani::cover call is unreachable because if the outer if condition holds, then the non-empty range r2 is strictly larger than the non-empty range r1, in which case, the condition in the inner if condition is impossible.

+
#[kani::unwind(6)]
+fn cover_unreachable_example() {
+    let r1: std::ops::Range<i32> = kani::any()..kani::any();
+    let r2: std::ops::Range<i32> = kani::any()..kani::any();
+    kani::assume(!r1.is_empty());
+    kani::assume(!r2.is_empty());
+    if r2.start > r1.end {
+        if r2.end < r1.end {
+            kani::cover!(r2.contains(&0));
+        }
+    }
+}
+
+
Check 3: cover_unreachable_example.cover.1
+         - Status: UNREACHABLE
+         - Description: "cover condition: r2.contains(&0)"
+         - Location: src/main.rs:90:13 in function cover_unreachable_example
+
+
    +
  1. UNDETERMINED: This is the same as the UNDETERMINED result for normal checks (see [check_results]).
  2. +
+

Verification summary

+

Kani reports a summary at the end of the verification report, which includes the overall results of all checks, the overall results of cover properties (if the package includes cover properties), and the overall verification result, e.g.:

+
SUMMARY:
+ ** 0 of 786 failed (41 unreachable)
+
+ ** 0 of 1 cover properties satisfied
+
+
+VERIFICATION:- SUCCESSFUL
+
+

Crates documentation

+

Kani currently ships with a kani crate that provide APIs to allow users to +write and configure their harnesses. +These APIs are tightly coupled with each Kani version, so they are not +published yet at https://crates.io.

+

You can find their latest documentation here:

+
    +
  • kani: This crate +provide APIs to write Kani harnesses.
  • +
+

Tutorial

+
+

NOTE: This tutorial expects you to have followed the Kani installation instructions first.

+
+

This tutorial will step you through a progression from simple to moderately complex tasks with Kani. +It's meant to ensure you can get started, and see at least some simple examples of how typical proofs are structured. +It will also teach you the basics of "debugging" proof harnesses, which mainly consists of diagnosing and resolving non-termination issues with the solver.

+

You may also want to read the Application section to better +understand what Kani is and how it can be applied on real code.

+

First steps

+

Kani is unlike the testing tools you may already be familiar with. +Much of testing is concerned with thinking of new corner cases that need to be covered. +With Kani, all the corner cases are covered from the start, and the new concern is narrowing down the scope to something manageable for the verifier.

+

Consider this first program (which can be found under first-steps-v1):

+
fn estimate_size(x: u32) -> u32 {
+    if x < 256 {
+        if x < 128 {
+            return 1;
+        } else {
+            return 3;
+        }
+    } else if x < 1024 {
+        if x > 1022 {
+            panic!("Oh no, a failing corner case!");
+        } else {
+            return 5;
+        }
+    } else {
+        if x < 2048 {
+            return 7;
+        } else {
+            return 9;
+        }
+    }
+}
+
+

Think about the test harness you would need to write to test this function. +You would need figure out a whole set of arguments to call the function with that would exercise each branch. +You would also need to keep that test harness up-to-date with the code, in case some of the branches change. +And if this function was more complicated—for example, if some of the branches depended on global state—the test harness would be even more onerous to write.

+

We can try to property test a function like this, but if we're naive about it (and consider all possible u32 inputs), then it's unlikely we'll ever find the bug.

+
    proptest! {
+        #![proptest_config(ProptestConfig::with_cases(10000))]
+        #[test]
+        fn doesnt_crash(x: u32) {
+            estimate_size(x);
+        }
+    }
+
+
# cargo test
+[...]
+test tests::doesnt_crash ... ok
+
+

There's only 1 in 4 billion inputs that fail, so it's vanishingly unlikely the property test will find it, even with a million samples.

+

Let's write a Kani proof harness for estimate_size. +This is a lot like a test harness, but now we can use kani::any() to represent all possible u32 values:

+
#[cfg(kani)]
+#[kani::proof]
+fn check_estimate_size() {
+    let x: u32 = kani::any();
+    estimate_size(x);
+}
+
+
# cargo kani
+[...]
+Runtime decision procedure: 0.00116886s
+
+RESULTS:
+Check 3: estimate_size.assertion.1
+         - Status: FAILURE
+         - Description: "Oh no, a failing corner case!"
+[...]
+VERIFICATION:- FAILED
+
+

Kani has immediately found a failure. +Notably, we haven't had to write explicit assertions in our proof harness: by default, Kani will find a host of erroneous conditions which include a reachable call to panic or a failing assert. +If Kani had run successfully on this harness, this amounts to a mathematical proof that there is no input that could cause a panic in estimate_size.

+
+

By default, Kani only reports failures, not how the failure happened. +In this example, it would be nice to get a concrete example of a value of x that triggers the failure. +Kani offers an (experimental) concrete playback feature that serves this purpose. +As an exercise, try applying concrete playback to this example and see what Kani outputs.

+
+

Exercise: Try other failures

+

We put an explicit panic in this function, but it's not the only kind of failure Kani will find. +Try a few other types of errors.

+

For example, instead of panicking we could try explicitly dereferencing a null pointer:

+
unsafe { return *(0 as *const u32) };
+
+

Notably, however, the Rust compiler emits a warning here:

+
warning: dereferencing a null pointer
+  --> src/lib.rs:10:29
+   |
+10 |    unsafe { return *(0 as *const u32) };
+   |                    ^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
+   |
+   = note: `#[warn(deref_nullptr)]` on by default
+
+

Still, it's just a warning, and we can run the code without test failures just as before. +But Kani still catches the issue:

+
[...]
+RESULTS:
+[...]
+Check 2: estimate_size.pointer_dereference.1
+         - Status: FAILURE
+         - Description: "dereference failure: pointer NULL"
+[...]
+VERIFICATION:- FAILED
+
+

Exercise: Can you find an example where the Rust compiler will not complain, and Kani will?

+
+Click to show one possible answer +
return 1 << x;
+
+

Overflow (in addition, multiplication or, in this case, bit-shifting by too much) is also caught by Kani:

+
RESULTS:
+[...]
+Check 1: estimate_size.assertion.1
+         - Status: FAILURE
+         - Description: "attempt to shift left with overflow"
+
+Check 3: estimate_size.undefined-shift.1
+         - Status: FAILURE
+         - Description: "shift distance too large"
+[...]
+VERIFICATION:- FAILED
+
+
+

Assertions, Assumptions, and Harnesses

+

It seems a bit odd that our example function is tested against billions of possible inputs, when it really only seems to be designed to handle a few thousand. +Let's encode this fact about our function by asserting some reasonable upper bound on our input, after we've fixed our bug. +(New code available under first-steps-v2):

+
fn estimate_size(x: u32) -> u32 {
+    assert!(x < 4096);
+
+    if x < 256 {
+        if x < 128 {
+            return 1;
+        } else {
+            return 3;
+        }
+    } else if x < 1024 {
+        if x > 1022 {
+            return 4;
+        } else {
+            return 5;
+        }
+    } else {
+        if x < 2048 {
+            return 7;
+        } else {
+            return 9;
+        }
+    }
+}
+
+

Now we've explicitly stated our previously implicit expectation: this function should never be called with inputs that are too big. +But if we attempt to verify this modified function, we run into a problem:

+
[...]
+RESULTS:
+[...]
+Check 3: estimate_size.assertion.1
+         - Status: FAILURE
+         - Description: "assertion failed: x < 4096"
+[...]
+VERIFICATION:- FAILED
+
+

What we want is a precondition for estimate_size. +That is, something that should always be true every time we call the function. +By putting the assertion at the beginning, we ensure the function immediately fails if that expectation is not met.

+

But our proof harness will still call this function with any integer, even ones that just don't meet the function's preconditions. +That's... not a useful or interesting result. +We know that won't work already. +How do we go back to successfully verifying this function?

+

This is the purpose of writing a proof harness. +Much like property testing (which would also fail in this assertion), we need to set up our preconditions, call the function in question, then assert our postconditions. +Here's a revised example of the proof harness, one that now succeeds:

+
#[cfg(kani)]
+#[kani::proof]
+fn verify_success() {
+    let x: u32 = kani::any();
+    kani::assume(x < 4096);
+    let y = estimate_size(x);
+    assert!(y < 10);
+}
+
+

Summary

+

In this section:

+
    +
  1. We saw Kani find panics, assertion failures, and even some other failures like unsafe dereferencing of null pointers.
  2. +
  3. We saw Kani find failures that testing could not easily find.
  4. +
  5. We saw how to write a proof harness and use kani::any().
  6. +
  7. We saw how proof harnesses are used to set up preconditions with kani::assume().
  8. +
+

Failures that Kani can spot

+

In the last section, we saw Kani spot two major kinds of failures: assertions and panics. +If the proof harness allows some program execution that results in a panic, then Kani will report that as a failure. +In addition, we saw (very briefly) a couple of other kinds of failures: null pointer dereferences and overflows. +In this section, we're going to expand on these additional checks, to give you an idea of what other problems Kani will find.

+

Bounds checking and pointers

+

Rust is safe by default, and so includes dynamic (run-time) bounds checking where needed. +Consider this Rust code (available here):

+
/// Wrap "too-large" indexes back into a valid range for the array
+fn get_wrapped(i: usize, a: &[u32]) -> u32 {
+    if a.len() == 0 {
+        return 0;
+    }
+    return a[i % a.len() + 1];
+}
+
+

We can again write a simple property test against this code:

+
    proptest! {
+        #[test]
+        fn doesnt_crash(i: usize, a: Vec<u32>) {
+            get_wrapped(i, &a);
+        }
+    }
+
+

This property test will immediately find a failing case, thanks to Rust's built-in bounds checking.

+

But what if we change this function to use unsafe Rust?

+
return unsafe { *a.as_ptr().add(i % a.len() + 1) };
+
+

Now the error becomes invisible to this test:

+
# cargo test
+[...]
+test bounds_check::tests::doesnt_crash ... ok
+
+

The property test still causes an out-of-bounds access, but this undefined behavior does not necessarily cause an immediate crash. +(This is part of why undefined behavior is so difficult to debug.) +Through the use of unsafe code, we removed the runtime check for an out of bounds access. +It just turned out that none of the randomly generated tests triggered behavior that actually crashed. +But if we write a Kani proof harness:

+
#[cfg(kani)]
+#[kani::proof]
+fn bound_check() {
+    let size: usize = kani::any();
+    kani::assume(size < 4096);
+    let index: usize = kani::any();
+    let array: Vec<u32> = vec![0; size];
+    get_wrapped(index, &array);
+}
+
+

And run this proof with:

+
cargo kani --harness bound_check
+
+

We still see a failure from Kani, even without Rust's runtime bounds checking.

+
+

Also, notice there were many checks in the verification output. +(At time of writing, 345.) +This is a result of using the standard library Vec implementation, which means our harness actually used quite a bit of code, short as it looks. +Kani is inserting a lot more checks than appear as asserts in our code, so the output can be large.

+
+

We get the following summary at the end:

+
SUMMARY: 
+ ** 1 of 345 failed (8 unreachable)
+Failed Checks: dereference failure: pointer outside object bounds
+ File: "./src/bounds_check.rs", line 11, in bounds_check::get_wrapped
+
+VERIFICATION:- FAILED
+
+

Notice that, for Kani, this has gone from a simple bounds-checking problem to a pointer-checking problem. +Kani will check operations on pointers to ensure they're not potentially invalid memory accesses. +Any unsafe code that manipulates pointers will, as we see here, raise failures if its behavior is actually a problem.

+

Consider trying a few more small exercises with this example:

+
    +
  1. Exercise: Switch back to the normal/safe indexing operation and re-try Kani. +How does Kani's output change, compared to the unsafe operation? +(Try predicting the answer, then seeing if you got it right.)
  2. +
  3. Exercise: Try Kani's experimental concrete playback feature on this example.
  4. +
  5. Exercise: Fix the error, run Kani, and see a successful verification.
  6. +
  7. Exercise: Try switching back to the unsafe code (now with the error fixed) and re-run Kani. Does it still verify successfully?
  8. +
+
+Click to see explanation for exercise 1 +

Having switched back to the safe indexing operation, Kani reports a bounds check failure:

+
SUMMARY:
+ ** 1 of 343 failed (8 unreachable)
+Failed Checks: index out of bounds: the length is less than or equal to the given index
+ File: "src/bounds_check.rs", line 11, in bounds_check::get_wrapped
+
+VERIFICATION:- FAILED
+
+
+
+Click to see explanation for exercise 2 +

cargo kani -Z concrete-playback --concrete-playback=inplace --harness bound_check produces the following test:

+
rust
+#[test]
+fn kani_concrete_playback_bound_check_4752536404478138800() {
+    let concrete_vals: Vec<Vec<u8>> = vec![
+        // 1ul
+        vec![1, 0, 0, 0, 0, 0, 0, 0],
+        // 18446744073709551615ul
+        vec![255, 255, 255, 255, 255, 255, 255, 255],
+    ];
+    kani::concrete_playback_run(concrete_vals, bound_check);
+}
+
+

which indicates that substituting the concrete values size = 1 and index = 2^64 in our proof harness will produce the out of bounds access.

+
+

Overflow and math errors

+

Consider a different variant on the function above:

+
fn get_wrapped(i: usize, a: &[u32]) -> u32 {
+    return a[i % a.len()];
+}
+
+

We've corrected the out-of-bounds access, but now we've omitted the "base case": what to return on an empty list. +Kani will spot this not as a bound error, but as a mathematical error: on an empty list the modulus operator (%) will cause a division by zero.

+
    +
  1. Exercise: Try to run Kani on this version of get_wrapped, to see what this kind of failure looks like.
  2. +
+

Rust can also perform runtime safety checks for integer overflows, much like it does for bounds checks. +(Though Rust disables this by default in --release mode, it can be re-enabled.) +Consider this code (available here):

+
fn simple_addition(a: u32, b: u32) -> u32 {
+    return a + b;
+}
+
+

A trivial function, but if we write a property test for it, we immediately find inputs where it fails, thanks to Rust's dynamic checks. +Kani will find these failures as well. +Here's the output from Kani:

+
# cargo kani --harness add_overflow
+[...]
+SUMMARY: 
+ ** 1 of 2 failed
+Failed Checks: attempt to add with overflow
+ File: "./src/overflow.rs", line 7, in overflow::simple_addition
+
+VERIFICATION:- FAILED
+
+

This issue can be fixed using Rust's alternative mathematical functions with explicit overflow behavior. +For instance, if the wrapping behavior is intended, you can write a.wrapping_add(b) instead of a + b. +Kani will then report no issues.

+

Exercise: Classic overflow failure

+

A classic example of a subtle bug that persisted in many implementations for a very long time is "finding the midpoint" in quick sort. +This often naively looks like this (code available here):

+
fn find_midpoint(low: u32, high: u32) -> u32 {
+    return (low + high) / 2;
+}
+
+
cargo kani --harness midpoint_overflow
+
+

Kani immediately spots the bug in the above code.

+
    +
  1. Exercise: Fix this function so it no longer overflows. +(Hint: depending on which approach you take, you may need to add the assumption that high > low to your proof harness. +Don't add that right away, see what happens if you don't. Just keep it in mind.)
  2. +
  3. Exercise: Prove your new implementation actually finds the midpoint correctly by adding an assertion to the test harness.
  4. +
+
+Click to see solutions for these exercises +

A very common approach for resolving the overflow issue looks like this:

+
return low + (high - low) / 2;
+
+

But if you naively try this (try it!), you'll find a new underflow error: high - low might result in a negative number, but has type u32. +Hence, the need to add the assumption we suggested above, to make that impossible. +(Adding an assumption, though, means there's a new way to "use it wrong." Perhaps we'd like to avoid that! Can you avoid the assumption?)

+

After that, you might wonder how to "prove your new implementation correct." +After all, what does "correct" even mean? +Often we're using a good approximation of correct, such as the equivalence of two implementations (often one much "simpler" than the other somehow). +Here's one possible assertion we could write in the proof harness:

+
assert!(result as u64 == (a as u64 + b as u64) / 2);
+
+

You might have even come up with this approach to avoiding the overflow issue in the first place! +Having two different implementations, using different approaches, but proven to yield the same results, gives us greater confidence that we compute the correct result.

+
+

Failures that Kani cannot spot

+

Check out Limitations for information on the checks that Kani does not perform. +Notably, Kani is not prioritizing all Rust-specific notions of undefined behavior.

+

Summary

+

In this section:

+
    +
  1. We saw Kani spot out-of-bounds accesses.
  2. +
  3. We saw Kani spot actually-unsafe dereferencing of a raw pointer to invalid memory.
  4. +
  5. We saw Kani spot a division by zero error and an overflowing addition.
  6. +
  7. As an exercise, we tried proving an assertion (finding the midpoint) that was not completely trivial.
  8. +
+

Loops, unwinding, and bounds

+

Consider code like this (available here):

+
fn initialize_prefix(length: usize, buffer: &mut [u8]) {
+    // Let's just ignore invalid calls
+    if length > buffer.len() {
+        return;
+    }
+
+    for i in 0..=length {
+        buffer[i] = 0;
+    }
+}
+
+

This code has an off-by-one error that only occurs on the last iteration of the loop (when called with an input that will trigger it). +We can try to find this bug with a proof harness like this:

+
#[cfg(kani)]
+#[kani::proof]
+#[kani::unwind(1)] // deliberately too low
+fn check_initialize_prefix() {
+    const LIMIT: usize = 10;
+    let mut buffer: [u8; LIMIT] = [1; LIMIT];
+
+    let length = kani::any();
+    kani::assume(length <= LIMIT);
+
+    initialize_prefix(length, &mut buffer);
+}
+
+

But we've just used a new attribute (#[kani::unwind(1)]) that requires some explanation. +When we run cargo kani on this code as we have written it, we see an odd verification failure:

+
SUMMARY:
+ ** 1 of 67 failed (66 undetermined)
+Failed Checks: unwinding assertion loop 0
+
+VERIFICATION:- FAILED
+
+

If we try removing this "unwind" annotation and re-running Kani, the result is worse: non-termination. +Kani simply doesn't produce a result.

+

The problem we're struggling with is the technique Kani uses to verify code. +We're not able to handle code with "unbounded" loops, and what "bounded" means can be quite subtle. +It has to have a constant number of iterations that's "obviously constant" enough for the verifier to actually figure this out. +In practice, very few loops are like this.

+

To verify programs like this with Kani as it exists today, we need to do two things:

+
    +
  1. Set an upper bound on the size of the problem. +We've actually already done part of this: our proof harness above seems to be trying to set an upper LIMIT of 10.
  2. +
  3. Tell Kani about this limit if (or when) it's not able to figure it out on its own. +This is the purpose of the kani::unwind annotation.
  4. +
+

Bounding proofs like this means we may no longer be proving as much as we originally hoped. +Who's to say, if we prove everything works up to size 10, that there isn't a novel bug lurking, reachable only with problems of size 11+? +Perhaps! +But, let's get back to the issue at hand.

+

By putting #[kani::unwind(1)] on the proof harness, we've placed an upper bound of 1 loop iteration. +The "unwinding assertion" failure that Kani reports is because this bound is not high enough. +The code tries to execute more than 1 loop iteration. +(And, because the unwinding isn't high enough, many of the other properties Kani is verifying become "undetermined": we don't really know if they're true or false, because we can't get far enough.)

+

Exercise: Try increasing the bound. Where might you start? How high do you need to go to get rid of the "unwinding assertion" failure?

+
+Click to see explanation for the exercise +

Since the proof harness is trying to limit the array to size 10, an initial unwind value of 10 seems like the obvious place to start. +But that's not large enough for Kani, and we still see the "unwinding assertion" failure.

+

At size 11, the "unwinding assertion" goes away, and now we can see the actual failure we're trying to find too. +We'll explain why we see this behavior in a moment.

+
+

Once we have increased the unwinding limit high enough, we're left with these failures:

+
SUMMARY:
+ ** 1 of 68 failed
+Failed Checks: index out of bounds: the length is less than or equal to the given index
+ File: "./src/lib.rs", line 12, in initialize_prefix
+
+VERIFICATION:- FAILED
+
+

Exercise: Fix the off-by-one error, and get the (bounded) proof to go through.

+

We now return to the question: why is 11 the unwinding bound?

+

Kani needs the unwinding bound to be "one more than" the number of loop iterations. +We previously had an off-by-one error that tried to do 11 iterations on an array of size 10. +So... the unwinding bound needed to be 11, then.

+
+

NOTE: Presently, there are some situations where "number of iterations of a loop" can be less obvious than it seems. +This can be easily triggered with use of break or continue within loops. +Often this manifests itself as needing "two more" or "three more" iterations in the unwind bound than seems like it would actually run. +In those situations, we might still need a bound like kani::unwind(13), despite looking like a loop bounded to 10 iterations.

+
+

The approach we've taken here is a general method for getting a bounded proof to go through:

+
    +
  1. Put an actual upper bound on the problem itself. +Here that's accomplished via LIMIT in our proof harness. +We don't create a slice any bigger than that, and that's what we loop over.
  2. +
  3. Start at a reasonable guess for a kani::unwind bound, and increase until the unwinding assertion failure goes away.
  4. +
  5. Or, if that starts to take too long to verify, decrease your problem's bound, to accommodate the verifier's performance.
  6. +
+

Unwinding value specification

+

The best approach to supplying Kani with unwind bounds is using the annotation kani::unwind, as we show above.

+

You might want to supply one via command line when experimenting, however. +In that case you can either use --default-unwind x to set an unwind bound for every proof harness that does not have an explicit bound.

+

Or you can override a harness's bound, but only when running a specific harness:

+
cargo kani --harness check_initialize_prefix --unwind 11
+
+

Finally, you might be interested in defaulting the unwind bound to 1, to force termination (and force supplying a bound) on all your proof harnesses. +You can do this by putting this into your Cargo.toml file:

+
[workspace.metadata.kani.flags]
+default-unwind = 1
+
+

Bounded proof

+

Before we finish, it's worth revisiting the implications of what we've done here. +Kani frequently needs to do "bounded proof", which contrasts with unbounded or full verification.

+

We've written a proof harness that shows initialize_prefix has no errors on input slices of size 10, but no higher. +The particular size we choose is usually determined by balancing the level of assurance we want, versus runtime of Kani. +It's often not worth running proofs for large numbers of iterations, unless either very high assurance is necessary, or there's reason to suspect larger problems will contain novel failure modes.

+

Exercise: Try increasing the problem size (both the unwind and the LIMIT constant). When does it start to take more than a few seconds?

+
+Click to see explanation for the exercise +

On your friendly neighborhood author's machine, a LIMIT of 100 takes about 3.8 seconds end-to-end. +This is a relatively simple bit of code, though, and it's not uncommon for some proofs to scale poorly even to 5 iterations.

+
+

One consequence of this, however, is that Kani often scales poorly to "big string problems" like parsing. +Often a parser will need to consume inputs larger than 10-20 characters to exhibit strange behaviors.

+

Summary

+

In this section:

+
    +
  1. We saw Kani fail to terminate.
  2. +
  3. We saw how #[kani::unwind(1)] can help force Kani to terminate (with a verification failure).
  4. +
  5. We saw "unwinding assertions" verify that we've set the unwinding limit high enough.
  6. +
  7. We saw how to put a practical bound on problem size in our proof harness.
  8. +
  9. We saw how to pick an unwinding size large enough to successfully verify that bounded proof.
  10. +
+

Nondeterministic variables

+

Kani is able to reason about programs and their execution paths by allowing users to create nondeterministic (also called symbolic) values using kani::any(). +Kani is a "bit-precise" model checker, which means that Kani considers all the possible bit-value combinations that would be valid if assigned to a variable's memory contents. +In other words, kani::any() should not produce values that are invalid for the type (which would lead to Rust undefined behavior).

+

Out of the box, Kani includes kani::any() implementations for most primitive and some std types. +In this tutorial, we will show how to use kani::any() to create symbolic values for other types.

+

Safe nondeterministic variables

+

Let's say you're developing an inventory management tool, and you would like to start verifying properties about your API. +Here is a simple example (available here):

+
use std::num::NonZeroU32;
+use vector_map::VecMap;
+
+pub type ProductId = u32;
+
+pub struct Inventory {
+    /// Every product in inventory must have a non-zero quantity
+    pub inner: VecMap<ProductId, NonZeroU32>,
+}
+
+impl Inventory {
+    pub fn update(&mut self, id: ProductId, new_quantity: NonZeroU32) {
+        self.inner.insert(id, new_quantity);
+    }
+
+    pub fn get(&self, id: &ProductId) -> Option<NonZeroU32> {
+        self.inner.get(id).cloned()
+    }
+}
+
+

Let's write a fairly simple proof harness, one that just ensures we successfully get the value we inserted with update:

+
    #[kani::proof]
+    #[kani::unwind(3)]
+    pub fn safe_update() {
+        // Empty to start
+        let mut inventory = Inventory { inner: VecMap::new() };
+
+        // Create non-deterministic variables for id and quantity.
+        let id: ProductId = kani::any();
+        let quantity: NonZeroU32 = kani::any();
+        assert!(quantity.get() != 0, "NonZeroU32 is internally a u32 but it should never be 0.");
+
+        // Update the inventory and check the result.
+        inventory.update(id, quantity);
+        assert!(inventory.get(&id).unwrap() == quantity);
+    }
+
+

We use kani::any() twice here:

+
    +
  1. id has type ProductId which was actually just a u32, and so any value is fine.
  2. +
  3. quantity, however, has type NonZeroU32. +In Rust, it would be undefined behavior to have a value of 0 for this type.
  4. +
+

We included an extra assertion that the value returned by kani::any() here was actually non-zero. +If we run this, you'll notice that verification succeeds.

+
cargo kani --harness safe_update
+
+

kani::any() is safe Rust, and so Kani only implements it for types where type invariants are enforced. +For NonZeroU32, this means we never return a 0 value. +The assertion we wrote in this harness was just an extra check we added to demonstrate this fact, not an essential part of the proof.

+

Custom nondeterministic types

+

While kani::any() is the only method Kani provides to inject non-determinism into a proof harness, Kani only ships with implementations for a few std types where we can guarantee safety. +When you need nondeterministic variables of types that don't have a kani::any() implementation available, you have the following options:

+
    +
  1. Implement the kani::Arbitrary trait for your type, so you and downstream crates can use kani::any() with your type.
  2. +
  3. Implement the bolero_generator::TypeGenerator trait. +This will enable you and downstream crates to use Kani via Bolero.
  4. +
  5. Write a function that builds an object from non-deterministic variables.
  6. +
+

We recommend the first approach for most cases. +The first approach is simple and conventional. This option will also enable you to use it with parameterized types, such as Option<MyType> and arrays. +Kani includes a derive macro that allows you to automatically derive kani::Arbitrary for structures and enumerations as long as all its fields also implement the kani::Arbitrary trait. +One downside of this approach today is that the kani crate ships with Kani, but it's not yet available on crates.io. +So you need to annotate the Arbitrary implementation with a #[cfg(kani)] attribute. +For the derive macro, use #[cfg_attr(kani, derive(kani::Arbitrary))].

+

The second approach is recommended for cases where you would also like to be able to apply fuzzing or property testing. +The benefits of doing so were described in this blog post. +Like kani::Arbitrary, this trait can also be used with a derive macro. +One thing to be aware of is that this type allow users to generate arbitrary values that include pointers. +In those cases, only the values pointed to are arbitrary, not the pointers themselves.

+

Finally, the last approach is recommended when you need to pass in parameters, like bounds on the size of the data structure. +(Which we'll discuss more in the next section.) +This approach is also necessary when you need to generate a nondeterministic variable of a type that you're importing from another crate, since Rust doesn't allow you to implement a trait defined in an external crate for a type that you don't own.

+

Either way, inside this function you would simply return an arbitrary value by generating arbitrary values for its components. +To generate a nondeterministic struct, you would just generate nondeterministic values for each of its fields. +For complex data structures like vectors or other containers, you can start with an empty one and add a (bounded) nondeterministic number of entries.

+

For example, for a simple enum you can just annotate it with the Arbitrary derive attribute:

+
#[derive(Copy, Clone)]
+#[cfg_attr(kani, derive(kani::Arbitrary))]
+pub enum Rating {
+    One,
+    Two,
+    Three,
+}
+
+

But if the same enum is defined in an external crate, you can use a simple trick:

+
    pub fn any_rating() -> Rating {
+        match kani::any() {
+            0 => Rating::One,
+            1 => Rating::Two,
+            _ => Rating::Three,
+        }
+    }
+
+

All we're doing here is making use of a nondeterministic integer to decide which variant of Rating to return.

+
+

NOTE: If we thought of this code as generating a random value, this function looks heavily biased. +We'd overwhelmingly generate a Three because it's matching "all other integers besides 1 and 2." +But Kani just see 3 meaningful possibilities, each of which is not treated any differently from each other. +The "proportion" of integers does not matter.

+
+

Bounding nondeterministic variables

+

You can use kani::any() for [T; N] (if implemented for T) because this array type has an exact and constant size. +But if you wanted a slice ([T]) up to size N, you can no longer use kani::any() for that. +Likewise, there is no implementation of kani::any() for more complex data structures like Vec.

+

The trouble with a nondeterministic vector is that you usually need to bound the size of the vector, for the reasons we investigated in the last chapter. +The kani::any() function does not have any arguments, and so cannot be given an upper bound.

+

This does not mean you cannot have a nondeterministic vector. +It just means you have to construct one. +Our example proof harness above constructs a nondeterministic Inventory of size 1, simply by starting with the empty Inventory and inserting a nondeterministic entry.

+

Exercise

+

Try writing a function to generate a (bounded) nondeterministic inventory (from the first example:)

+
fn any_inventory(bound: u32) -> Inventory {
+   // fill in here
+}
+
+

One thing you'll quickly find is that the bounds must be very small. +Kani does not (yet!) scale well to nondeterministic-size data structures involving heap allocations. +A proof harness like safe_update above, but starting with any_inventory(2) will probably take a couple of minutes to prove.

+

A hint for this exercise: you might choose two different behaviors, "size of exactly bound" or "size up to bound". +Try both!

+

A solution can be found in exercise_solution.rs.

+

Summary

+

In this section:

+
    +
  1. We saw how kani::any() will return "safe" values for each of the types Kani implements it for.
  2. +
  3. We saw how to implement kani::Arbitrary or just write a function to create nondeterministic values for other types.
  4. +
  5. We noted that some types cannot implement kani::any() as they need a bound on their size.
  6. +
  7. We did an exercise to generate nondeterministic values of bounded size for Inventory.
  8. +
+

Reference

+

This section is the main reference for Kani. +It contains sections that informally describe its main features.

+

Attributes

+

In Kani, attributes are used to mark functions as harnesses and control their execution. +This section explains the attributes available in Kani and how they affect the verification process.

+

At present, the available Kani attributes are the following:

+ +

#[kani::proof]

+

The #[kani::proof] attribute specifies that a function is a proof harness.

+

Proof harnesses are similar to test harnesses, especially property-based test harnesses, +and they may use functions from the Kani API (e.g., kani::any()). +A proof harness is the smallest verification unit in Kani.

+

When Kani is run, either through kani or cargo kani, it'll first collect all proof harnesses +(i.e., functions with the attribute #[kani::proof]) and then attempt to verify them.

+

Example

+

If we run Kani on this example:

+
#[kani::proof]
+fn my_harness() {
+    assert!(1 + 1 == 2);
+}
+
+

We should see a line in the output that says Checking harness my_harness... (assuming my_harness is the only harness in our code). +This will be followed by multiple messages that come from CBMC (the verification engine used by Kani) and the verification results.

+

Using any other Kani attribute without #[kani::proof] will result in compilation errors.

+

Limitations

+

The #[kani::proof] attribute can only be added to functions without parameters.

+

#[kani::should_panic]

+

The #[kani::should_panic] attribute specifies that a proof harness is expected to panic.

+

This attribute allows users to exercise negative verification. +It's analogous to how #[should_panic] allows users to exercise negative testing for Rust unit tests.

+

This attribute only affects the overall verification result. +In particular, using the #[kani::should_panic] attribute will return one of the following results:

+
    +
  • VERIFICATION:- FAILED (encountered no panics, but at least one was expected) if there were no failed checks.
  • +
  • VERIFICATION:- FAILED (encountered failures other than panics, which were unexpected) if there were failed checks but not all them were related to panics.
  • +
  • VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected) otherwise.
  • +
+

At the moment, to determine if a check is related to a panic, we check if its class is assertion. +The class is the second member in the property name, the triple that's printed after Check X: : <function>.<class>.<number>. +For example, the class in Check 1: my_harness.assertion.1 is assertion, so this check is considered to be related to a panic.

+
+

NOTE: The #[kani::should_panic] is only recommended for writing +harnesses which complement existing harnesses that don't use the same +attribute. In other words, it's only recommended to write negative harnesses +after having written positive harnesses that successfully verify interesting +properties about the function under verification.

+
+

Limitations

+

The #[kani::should_panic] attribute verifies that there are one or more failed checks related to panics. +At the moment, it's not possible to pin it down to specific panics. +Therefore, it's possible that the panics detected with #[kani::should_panic] aren't the ones that were originally expected after a change in the code under verification.

+

Example

+

Let's assume we're using the Device from this example:

+
struct Device {
+    is_init: bool,
+}
+
+impl Device {
+    fn new() -> Self {
+        Device { is_init: false }
+    }
+
+    fn init(&mut self) {
+        assert!(!self.is_init);
+        self.is_init = true;
+    }
+}
+
+

We may want to verify that calling device.init() more than once should result in a panic. +We can do so with the following harness:

+
#[kani::proof]
+#[kani::should_panic]
+fn cannot_init_device_twice() {
+    let mut device = Device::new();
+    device.init();
+    device.init();
+}
+
+

Running Kani on it will produce the result VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected)

+

#[kani::unwind(<number>)]

+

The #[kani::unwind(<number>)] attribute specifies that all loops must be unwound up to <number> times.

+

By default, Kani attempts to unwind all loops automatically. +However, this unwinding process doesn't always terminate. +The #[kani::unwind(<number>)] attribute will:

+
    +
  1. Disable automatic unwinding.
  2. +
  3. Unwind all loops up to <number> times.
  4. +
+

After the unwinding stage, Kani will attempt to verify the harness. +If the #[kani::unwind(<number>)] attribute was specified, there's a chance that one or more loops weren't unwound enough times. +In that case, there will be at least one failed unwinding assertion (there's one unwinding assertion for each loop), causing verification to fail.

+

Check the Loops, unwinding and bounds section for more information about unwinding.

+

Example

+

Let's assume we've written this code which contains a loop:

+
fn my_sum(vec: &Vec<u32>) -> u32 {
+    let mut sum = 0;
+    for elem in vec {
+        sum += elem;
+    }
+    sum
+}
+
+#[kani::proof]
+fn my_harness() {
+    let vec = vec![1, 2, 3];
+    let sum = my_sum(&vec);
+    assert!(sum == 6);
+}
+
+

Running this example on Kani will produce a successful verification result. +In this case, Kani automatically finds the required unwinding value (i.e., the number of times it needs to unwind all loops). +This means that the #[kani::unwind(<number>)] attribute isn't needed, as we'll see soon. +In general, the required unwinding value is equal to the maximum number of iterations for all loops, plus one. +The required unwinding value in this example is 4: the 3 iterations in the for elem in vec loop, plus 1.

+

Let's see what happens if we force a lower unwinding value with #[kani::unwind(3)]:

+
#[kani::proof]
+#[kani::unwind(3)]
+fn my_harness() {
+    let vec = vec![1, 2, 3];
+    let sum = my_sum(&vec);
+    assert!(sum == 6);
+}
+
+

As we mentioned, trying to verify this harness causes an unwinding failure:

+
SUMMARY:
+ ** 1 of 187 failed (186 undetermined)
+Failed Checks: unwinding assertion loop 0
+ File: "/home/ubuntu/devices/src/main.rs", line 32, in my_sum
+
+VERIFICATION:- FAILED
+[Kani] info: Verification output shows one or more unwinding failures.
+[Kani] tip: Consider increasing the unwinding value or disabling `--unwinding-assertions`.
+
+

Kani cannot verify the harness because there is at least one unwinding assertion failure. +But, if we use #[kani::unwind(4)], which is the right unwinding value we computed earlier:

+
#[kani::proof]
+#[kani::unwind(4)]
+fn my_harness() {
+    let vec = vec![1, 2, 3];
+    let sum = my_sum(&vec);
+    assert!(sum == 6);
+}
+
+

We'll get a successful result again:

+
SUMMARY:
+ ** 0 of 186 failed
+
+VERIFICATION:- SUCCESSFUL
+
+

#[kani::solver(<solver>)]

+

Changes the solver to be used by Kani's verification engine (CBMC).

+

This may change the verification time required to verify a harness.

+

At present, <solver> can be one of:

+
    +
  • minisat: MiniSat.
  • +
  • cadical (default): CaDiCaL.
  • +
  • kissat: kissat.
  • +
  • bin="<SAT_SOLVER_BINARY>": A custom solver binary, "<SAT_SOLVER_BINARY>", that must be in path.
  • +
+

Example

+

Kani will use the CaDiCaL solver in the following example:

+
#[kani::proof]
+#[kani::solver(cadical)]
+fn check() {
+    let mut a = [2, 3, 1];
+    a.sort();
+    assert_eq!(a[0], 1);
+    assert_eq!(a[1], 2);
+    assert_eq!(a[2], 3);
+}
+
+

Changing the solver may result in different verification times depending on the harness.

+

Note that the default solver may vary depending on Kani's version. +We highly recommend users to annotate their harnesses if the choice of solver +has a major impact on performance, even if the solver used is the current +default one.

+

#[kani::stub(<original>, <replacement>)]

+

Replaces the function/method with name with the function/method with name during compilation

+

Check the Stubbing section for more information about stubbing.

+

Experimental Features

+

We elaborate on some of the more commonly used experimental features in Kani. +This is not an exhaustive list; to see all of Kani's experimental features, run cargo kani --help. +To use an experimental feature, invoke Kani with the --unstable or -Z flag followed by the name of the feature.

+

Automatic Harness Generation

+

Recall the harness for estimate_size that we wrote in First Steps:

+
#[cfg(kani)]
+#[kani::proof]
+fn check_estimate_size() {
+    let x: u32 = kani::any();
+    estimate_size(x);
+}
+
+

This harness first declares a local variable x using kani::any(), then calls estimate_size with argument x. +Many proof harnesses follow this predictable format—to verify a function foo, we create arbitrary values for each of foo's arguments, then call foo on those arguments.

+

The autoharness subcommand leverages this observation to automatically generate and run harnesses. +Kani scans the crate for functions whose arguments all implement the kani::Arbitrary trait, generates harnesses for them, then runs them. +These harnesses are internal to Kani--i.e., Kani does not make any changes to your source code.

+

Usage

+

Run either:

+
# cargo kani autoharness -Z unstable-options
+
+

or

+
# kani autoharness -Z unstable-options <FILE>
+
+

If Kani detects that all of a function foo's arguments implement kani::Arbitrary, it will generate and run a #[kani::proof] harness, which prints:

+
Autoharness: Checking function foo against all possible inputs...
+<VERIFICATION RESULTS>
+
+

However, if Kani detects that foo has a contract, it will instead generate a #[kani::proof_for_contract] harness and verify the contract:

+
Autoharness: Checking function foo's contract against all possible inputs...
+<VERIFICATION RESULTS>
+
+

Kani generates and runs these harnesses internally—the user only sees the verification results.

+

The autoharness subcommand has options --include-function and --exclude-function to include and exclude particular functions. +These flags look for partial matches against the fully qualified name of a function.

+

For example, if a module my_module has many functions, but we are only interested in my_module::foo and my_module::bar, we can run:

+
cargo run autoharness -Z unstable-options --include-function foo include-function bar
+
+

To exclude my_module entirely, run:

+
cargo run autoharness -Z unstable-options --exclude-function my_module
+
+

Example

+

Using the estimate_size example from First Steps again:

+
fn estimate_size(x: u32) -> u32 {
+    if x < 256 {
+        if x < 128 {
+            return 1;
+        } else {
+            return 3;
+        }
+    } else if x < 1024 {
+        if x > 1022 {
+            panic!("Oh no, a failing corner case!");
+        } else {
+            return 5;
+        }
+    } else {
+        if x < 2048 {
+            return 7;
+        } else {
+            return 9;
+        }
+    }
+}
+
+

We get:

+
# cargo kani autoharness -Z unstable-options
+Autoharness: Checking function estimate_size against all possible inputs...
+RESULTS:
+Check 3: estimate_size.assertion.1
+         - Status: FAILURE
+         - Description: "Oh no, a failing corner case!"
+[...]
+
+Verification failed for - estimate_size
+Complete - 0 successfully verified functions, 1 failures, 1 total.
+
+

Request for comments

+

This feature is experimental and is therefore subject to change. +If you have ideas for improving the user experience of this feature, +please add them to this GitHub issue.

+

Limitations

+

Kani will only generate an automatic harness for a function if it can determine that all of the function's arguments implement Arbitrary. +It does not attempt to derive/implement Arbitrary for any types, even if those types could implement Arbitrary.

+

If a function contains a loop with a loop contract, Kani will detect the presence of a loop contract and verify that contract. +If, however, the loop does not have a contract, then there is currently no way to specify an unwinding bound for the function, meaning that Kani may hang as it tries to unwind the loop. +We recommend using the --exclude-function option to exclude any functions that have this issue (or --harness-timeout to bail after attempting verification for some amount of time).

+

Coverage

+

Recall our estimate_size example from First steps, +where we wrote a proof harness constraining the range of inputs to integers less than 4096:

+
#[cfg(kani)]
+#[kani::proof]
+fn verify_success() {
+    let x: u32 = kani::any();
+    kani::assume(x < 4096);
+    let y = estimate_size(x);
+    assert!(y < 10);
+}
+
+

We must wonder if we've really fully tested our function. +What if we revise the function, but forget to update the assumption in our proof harness to cover the new range of inputs?

+

Fortunately, Kani is able to report a coverage metric for each proof harness. +In the first-steps-v2 directory, try running:

+
cargo kani --coverage -Z source-coverage --harness verify_success
+
+

which verifies the harness, then prints coverage information for each line. +In this case, we see that each line of estimate_size is followed by FULL, indicating that our proof harness provides full coverage.

+

Try changing the assumption in the proof harness to x < 2048. +Now the harness won't be testing all possible cases. +Rerun the command. +You'll see this line:

+
src/lib.rs, 24, NONE
+
+

which indicates that the proof no longer covers line 24, which addresses the case where x >= 2048.

+

Stubbing

+

Stubbing (or mocking) is an unstable feature which allows users to specify that certain items should be replaced with stubs (mocks) of those items during verification. +At present, the only items where stubbing can be applied are functions and methods (see limitations for more details).

+

When to consider stubbing

+

In general, we have identified three reasons where users may consider stubbing:

+
    +
  • Unsupported features: The code under verification contains features that Kani does not support, such as inline assembly.
  • +
  • Bad performance: The code under verification contains features that Kani supports, but it leads to bad verification performance (for example, deserialization code).
  • +
  • Compositional reasoning: The code under verification contains code that has been verified separately. +Stubbing the code that has already been verified with a less complex version that mimics its behavior can result in reduced verification workloads.
  • +
+

In most cases, stubbing enables users to verify code that otherwise would be impractical to verify. +Although definitions for mocking (normally used in testing) and stubbing may slightly differ depending on who you ask, we often use both terms interchangeably.

+

Components

+

The stubbing feature can be enabled by using the --enable-stubbing option when calling Kani. +Since it's an unstable feature, it requires passing the --enable-unstable option in addition to --enable-stubbing.

+

At present, the only component of the stubbing feature is the #[kani::stub(<original>, <replacement>)] attribute, +which allows you to specify the pair of functions/methods that must be stubbed in a harness.

+ +

The #[kani::stub(...)] attribute

+

The stub attribute #[kani::stub(<original>, <replacement>)] is the main tool of the stubbing feature.

+

It indicates to Kani that the function/method with name <original> should be replaced with the function/method with name <replacement> during the compilation step. +The names of these functions/methods are resolved using Rust's standard name resolution rules. +This includes support for imports like use foo::bar as baz, as well as imports of multiple versions of the same crate.

+

This attribute must be specified on a per-harness basis. This provides a high degree of flexibility for users, since they are given the option to stub the same item with different replacements (or not use stubbing at all) depending on the proof harness. In addition, the attribute can be specified multiple times per harness, so that multiple (non-conflicting) stub pairings are supported.

+

An example: stubbing random

+

Let's see a simple example where we use the rand::random function +to generate an encryption key.

+
#[cfg(kani)]
+#[kani::proof]
+fn encrypt_then_decrypt_is_identity() {
+    let data: u32 = kani::any();
+    let encryption_key: u32 = rand::random();
+    let encrypted_data = data ^ encryption_key;
+    let decrypted_data = encrypted_data ^ encryption_key;
+    assert_eq!(data, decrypted_data);
+}
+
+
+

At present, Kani fails to verify this example due to issue #1781.

+

However, we can work around this limitation thanks to the stubbing feature:

+
#[cfg(kani)]
+fn mock_random<T: kani::Arbitrary>() -> T {
+    kani::any()
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::stub(rand::random, mock_random)]
+fn encrypt_then_decrypt_is_identity() {
+    let data: u32 = kani::any();
+    let encryption_key: u32 = rand::random();
+    let encrypted_data = data ^ encryption_key;
+    let decrypted_data = encrypted_data ^ encryption_key;
+    assert_eq!(data, decrypted_data);
+}
+
+

Here, the #[kani::stub(rand::random, mock_random)] attribute indicates to Kani that it should replace rand::random with the stub mock_random. +Note that this is a fair assumption to do: rand::random is expected to return any u32 value, just like kani::any.

+

Now, let's run it through Kani:

+
cargo kani --enable-unstable --enable-stubbing --harness encrypt_then_decrypt_is_identity
+
+

The verification result is composed of a single check: the assertion corresponding to assert_eq!(data, decrypted_data).

+
RESULTS:
+Check 1: encrypt_then_decrypt_is_identity.assertion.1
+         - Status: SUCCESS
+         - Description: "assertion failed: data == decrypted_data"
+         - Location: src/main.rs:18:5 in function encrypt_then_decrypt_is_identity
+
+
+SUMMARY:
+ ** 0 of 1 failed
+
+VERIFICATION:- SUCCESSFUL
+
+

Kani shows that the assertion is successful, avoiding any issues that appear if we attempt to verify the code without stubbing.

+

Limitations

+

In the following, we describe all the limitations of the stubbing feature.

+

Usage restrictions

+

The usage of stubbing is limited to the verification of a single harness. +Therefore, users are required to pass the --harness option when using the stubbing feature.

+

In addition, this feature isn't compatible with concrete playback.

+

Support

+

Support for stubbing is currently limited to functions and methods. All other items aren't supported.

+

The following are examples of items that could be good candidates for stubbing, but aren't supported:

+
    +
  • Types
  • +
  • Macros
  • +
  • Traits
  • +
  • Intrinsics
  • +
+

We acknowledge that support for method stubbing isn't as ergonomic as it could be. +A common problem when attempting to define method stubs is that we don't have access to the private fields of an object (i.e., the fields in self). +One workaround is to use the unsafe function std::mem::transmute, as in this example:

+
struct Foo {
+    x: u32,
+}
+
+impl Foo {
+    pub fn m(&self) -> u32 {
+        0
+    }
+}
+
+struct MockFoo {
+    pub x: u32,
+}
+
+fn mock_m(foo: &Foo) -> u32 {
+    let mock: &MockFoo = unsafe { std::mem::transmute(foo) };
+    return mock.x;
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::stub(Foo::m, mock_m)]
+fn my_harness() { ... }
+
+

However, this isn't recommended since it's unsafe and error-prone. +In general, we don't recommend stubbing for private functions/methods. +Doing so can lead to brittle proofs: private functions/methods are subject to change or removal even in version minor upgrades (they aren't part of the APIs). +Therefore, proofs that rely on stubbing for private functions/methods might incur a high maintenance burden.

+

Error conditions

+

Given a set of original-replacement pairs, Kani will exit with an error if:

+
    +
  1. a specified original function does not exist;
  2. +
  3. a specified replacement stub does not exist;
  4. +
  5. the user specifies conflicting stubs for the same harness (e.g., if the same original function is mapped to multiple replacement functions); or
  6. +
  7. the signature of the replacement stub is not compatible with the signature of the original function/method (see next section).
  8. +
+

Stub compatibility and validation

+

We consider a stub and a function/method to be compatible if all the following conditions are met:

+
    +
  • They have the same number of parameters.
  • +
  • They have the same return type.
  • +
  • Each parameter in the stub has the same type as the corresponding parameter in the original function/method.
  • +
  • The stub must have the same number of generic parameters as the original function/method. +However, a generic parameter in the stub is allowed to have a different name than the corresponding parameter in the original function/method. +For example, the stub bar<A, B>(x: A, y: B) -> B is considered to have a type compatible with the function foo<S, T>(x: S, y: T) -> T.
  • +
  • The bounds for each type parameter don't need to match; however, all calls to the original function must also satisfy the bounds of the stub.
  • +
+

The final point is the most subtle. +We don't require that a type parameter in the signature of the stub implements the same traits as the corresponding type parameter in the signature of the original function/method. +However, Kani will reject a stub if a trait mismatch leads to a situation where a statically dispatched call to a trait method cannot be resolved during monomorphization. +For example, this restriction rules out the following harness:

+
fn foo<T>(_x: T) -> bool {
+    false
+}
+
+trait DoIt {
+    fn do_it(&self) -> bool;
+}
+
+fn bar<T: DoIt>(x: T) -> bool {
+    x.do_it()
+}
+
+#[kani::proof]
+#[kani::stub(foo, bar)]
+fn harness() {
+    assert!(foo("hello"));
+}
+
+

The call to the trait method DoIt::do_it is unresolvable in the stub bar when the type parameter T is instantiated with the type &str. +On the other hand, this approach provides some flexibility, such as allowing our earlier example of mocking rand::random: +both rand::random and my_random have type () -> T, but in the first case T is restricted such that the type Standard implements Distribution<T>, +whereas in the latter case T has to implement kani::Arbitrary. +This trait mismatch is allowed because at this call site T is instantiated with u32, which implements kani::Arbitrary.

+

Contracts

+

Consider the following example:

+
fn gcd(mut max: u8, mut min: u8) -> u8 {
+    if min > max {
+        std::mem::swap(&mut max, &mut min);
+    }
+
+    let rest = max % min;
+    if rest == 0 { min } else { gcd(min, rest) }
+}
+
+

Let's assume we want to verify some code that calls gcd. +In the worst case, the number of steps (recursions) in gcd approaches 1.5 times the number of bits needed to represent the input numbers. +So, for two large 64-bit numbers, a single call to gcd can take almost 96 iterations. +It would be very expensive for Kani to unroll each of these iterations and then perform symbolic execution.

+

Instead, we can write contracts with guarantees about gcd's behavior. +Once Kani verifies that gcd's contracts are correct, it can replace each invocation of gcd with its contracts, which reduces verification time for gcd's callers. +For example, perhaps we want to ensure that the returned result does indeed divide both max and min. +In that case, we could write contracts like these:

+
#[kani::requires(min != 0 && max != 0)]
+#[kani::ensures(|result| *result != 0 && max % *result == 0 && min % *result == 0)]
+#[kani::recursion]
+fn gcd(mut max: u8, mut min: u8) -> u8 { ... }
+
+

Since gcd performs max % min (and perhaps swaps those values), passing zero as an argument could cause a division by zero. +The requires contract tells Kani to restrict the range of nondeterministic inputs to nonzero ones so that we don't run into this error. +The ensures contract is what actually checks that the result is a correct divisor for the inputs. +(The recursion attribute is required when using contracts on recursive functions).

+

Then, we would write a harness to verify those contracts, like so:

+
#[kani::proof_for_contract(gcd)]
+fn check_gcd() {
+    let max: u8 = kani::any();
+    let min: u8 = kani::any();
+    gcd(max, min);
+}
+
+

and verify it by running kani -Z function-contracts.

+

Once Kani verifies the contracts, we can use Kani's stubbing feature to replace all invocations to gcd with its contracts, for instance:

+
// Assume foo() invokes gcd().
+// By using stub_verified, we tell Kani to replace 
+// invocations of gcd() with its verified contracts.
+#[kani::proof]
+#[kani::stub_verified(gcd)]
+fn check_foo() {
+    let x: u8 = kani::any();
+    foo(x);
+}
+
+

By leveraging the stubbing feature, we can replace the (expensive) gcd call with a verified abstraction of its behavior, greatly reducing verification time for foo.

+

There is far more to learn about contracts. +We highly recommend reading our blog post about contracts (from which this gcd example is taken). We also recommend looking at the contracts module in our documentation.

+

Loop Contracts

+

Loop contract are used to specify invariants for loops for the sake of extending Kani's bounded proofs to unbounded proofs. +A loop invariant is an expression that holds upon entering a loop and after every execution of the loop body. +It captures something that does not change about every step of the loop.

+

It is worth revisiting the discussion about bounded proof and +loop unwinding. In short, bounds on the number of times Kani unwinds loops also bound the size of inputs, +and hence result in a bounded proof. +Loop contracts are used to abstract out loops as non-loop blocks to avoid loop unwinding, and hence remove the bounds on the inputs.

+

Consider the following example:

+
fn simple_loop() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    while x > 1 {
+        x = x - 1;
+    }
+
+    assert!(x == 1);
+}
+
+

In this program, the loop repeatedly decrements x until it equals 1. Because we haven't specified an upper bound for x, to verify this function, +Kani needs to unwind the loop for u64::MAX iterations, which is computationally expensive. Loop contracts allow us to abstract the loop behavior, significantly reducing the verification cost.

+

With loop contracts, we can specify the loop’s behavior using invariants. For example:

+
#![feature(stmt_expr_attributes)]
+#![feature(proc_macro_hygiene)]
+
+fn simple_loop_with_loop_contracts() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    #[kani::loop_invariant(x >= 1)]
+    while x > 1 {
+        x = x - 1;
+    }
+
+    assert!(x == 1);
+}
+
+

Here, the loop invariant #[kani::loop_invariant(x >= 1)] specifies that the condition x >= 1 must hold true at the start of each iteration before the loop guard is +checked. Once Kani verifies that the loop invariant is inductive, it will use the invariant to abstract the loop and avoid unwinding.

+

Now let's run the proof with loop contracts through kani:

+
kani simple_loop_with_loop_contracts.rs  -Z loop-contracts
+
+

The output reported by Kani on the example will be

+
...
+
+
+Check 10: simple_loop_with_loop_contracts.loop_invariant_base.1
+         - Status: SUCCESS
+         - Description: "Check invariant before entry for loop simple_loop_with_loop_contracts.0"
+         - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts
+
+Check 11: simple_loop_with_loop_contracts.loop_assigns.1
+         - Status: SUCCESS
+         - Description: "Check assigns clause inclusion for loop simple_loop_with_loop_contracts.0"
+         - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts
+
+Check 13: simple_loop_with_loop_contracts.assigns.1
+         - Status: SUCCESS
+         - Description: "Check that x is assignable"
+         - Location: simple_while_loop.rs:17:9 in function simple_loop_with_loop_contracts
+
+Check 14: simple_loop_with_loop_contracts.loop_invariant_step.1
+         - Status: SUCCESS
+         - Description: "Check invariant after step for loop simple_loop_with_loop_contracts.0"
+         - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts
+
+Check 15: simple_loop_with_loop_contracts.loop_invariant_step.2
+         - Status: SUCCESS
+         - Description: "Check invariant after step for loop simple_loop_with_loop_contracts.0"
+         - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts
+
+...
+
+SUMMARY:
+ ** 0 of 99 failed
+
+VERIFICATION:- SUCCESSFUL
+Verification Time: 0.3897019s
+
+Complete - 1 successfully verified harnesses, 0 failures, 1 total.
+
+

Loop contracts for while loops

+

Syntax

+
+

#[kani::loop_invariant( Expression )]

+

while Expressionexcept struct expression BlockExpression

+
+

An invariant contract #[kani::loop_invariant(cond)] accepts a valid Boolean expression cond over the variables visible at the same scope as the loop.

+

Semantics

+

A loop invariant contract expands to several assumptions and assertions:

+
    +
  1. The invariant is asserted just before the first iteration.
  2. +
  3. The invariant is assumed on a non-deterministic state to model a non-deterministic iteration.
  4. +
  5. The invariant is finally asserted again to establish its inductiveness.
  6. +
+

Mathematical induction is the working principle here. (1) establishes the base case for induction, and (2) & (3) establish the inductive case. +Therefore, the invariant must hold after the loop execution for any number of iterations. The invariant, together with the negation of the loop guard, +must be sufficient to establish subsequent assertions. If it is not, the abstraction is too imprecise and the user must supply a stronger invariant.

+

To illustrate the key idea, we show how Kani abstracts the loop in simple_loop_with_loop_contracts as a non-loop block:

+
assert!(x >= 1) // check loop invariant for the base case.
+x = kani::any();
+kani::assume(x >= 1);
+if x > 1 {
+    // proof path 1:
+    //   both loop guard and loop invariant are satisfied.
+    x = x - 1;
+    assert!(x >= 1); // check that loop invariant is inductive.
+    kani::assume(false) // block this proof path.
+}
+// proof path 2:
+//   loop invariant is satisfied and loop guard is violated.
+assert!(x == 1);
+
+

That is, we assume that we are in an arbitrary iteration after checking that the loop invariant holds for the base case. With the inductive hypothesis (kani::assume(x >= 1);), +we will either enter the loop (proof path 1) or leave the loop (proof path 2). We prove the two paths separately by killing path 1 with kani::assume(false);. +Note that all assertions after kani::assume(false) will be ignored as false => p can be deduced as true for any p.

+

In proof path 1, we prove properties inside the loop and at last check that the loop invariant is inductive.

+

In proof path 2, we prove properties after leaving the loop. As we leave the loop only when the loop guard is violated, the post condition of the loop can be expressed as +!guard && inv, which is x <= 1 && x >= 1 in the example. The postcondition implies x == 1—the property we want to prove at the end of simple_loop_with_loop_contracts.

+

Limitations

+

Loop contracts comes with the following limitations.

+
    +
  1. Only while loops are supported. The other three kinds of loops are not supported: loop loops +, while let loops, and for loops.
  2. +
  3. Kani infers loop modifies with alias analysis. Loop modifies are those variables we assume to be arbitrary in the inductive hypothesis, and should cover all memory locations that are written to during +the execution of the loops. A proof will fail if the inferred loop modifies misses some targets written in the loops. +We observed this happens when some fields of structs are modified by some other functions called in the loops.
  4. +
  5. Kani doesn't check if a loop will always terminate in proofs with loop contracts. So it could be that some properties are proved successfully with Kani but actually are unreachable due to the +non-termination of some loops.
  6. +
  7. We don't check if loop invariants are side-effect free. A loop invariant with a side effect could lead to an unsound proof result. Make sure that the specified loop contracts are side-effect free.
  8. +
+

Concrete Playback

+

When the result of a certain check comes back as a FAILURE, Kani offers the concrete-playback option to help debug. This feature generates a Rust unit test case that plays back a failing proof harness using a concrete counterexample.

+

When concrete playback is enabled, Kani will generate unit tests for assertions that failed during verification, +as well as cover statements that are reachable.

+

These tests can then be executed using Kani's playback subcommand.

+

Usage

+

In order to enable this feature, run Kani with the -Z concrete-playback --concrete-playback=[print|inplace] flag. +After getting a verification failure, Kani will generate a Rust unit test case that plays back a failing +proof harness with a concrete counterexample. +The concrete playback modes mean the following:

+
    +
  • print: Kani will just print the unit test to stdout. +You will then need to copy this unit test into the same module as your proof harness. +This is also helpful if you just want to quickly find out which values were assigned by kani::any() calls.
  • +
  • inplace: Kani will automatically copy the unit test into your source code. +Before running this mode, you might find it helpful to have your existing code committed to git. +That way, you can easily remove the unit test with git revert. +Note that Kani will not copy the unit test into your source code if it detects +that the exact same test already exists.
  • +
+

After the unit test is in your source code, you can run it with the playback subcommand. +To debug it, there are a couple of options:

+ +

To manually compile and run the test, you can use Kani's playback subcommand:

+
cargo kani playback -Z concrete-playback -- ${unit_test_func_name}
+
+

The output from this command is similar to cargo test. +The output will have a line in the beginning like +Running unittests {files} ({binary}).

+

You can further debug the binary with tools like rust-gdb or lldb.

+

Example

+

Running kani -Z concrete-playback --concrete-playback=print on the following source file:

+
#[kani::proof]
+fn proof_harness() {
+    let a: u8 = kani::any();
+    let b: u16 = kani::any();
+    assert!(a / 2 * 2 == a &&
+            b / 2 * 2 == b);
+}
+
+

yields a concrete playback Rust unit test similar to the one below:

+
#[test]
+fn kani_concrete_playback_proof_harness_16220658101615121791() {
+    let concrete_vals: Vec<Vec<u8>> = vec![
+        // 133
+        vec![133],
+        // 35207
+        vec![135, 137],
+    ];
+    kani::concrete_playback_run(concrete_vals, proof_harness);
+}
+
+

Here, 133 and 35207 are the concrete values that, when substituted for a and b, +cause an assertion failure. +vec![135, 137] is the byte array representation of 35207.

+

Request for comments

+

This feature is experimental and is therefore subject to change. +If you have ideas for improving the user experience of this feature, +please add them to this GitHub issue. +We are tracking the existing feature requests in +this GitHub milestone.

+

Limitations

+
    +
  • This feature does not generate unit tests for failing non-panic checks (e.g., UB checks). +This is because checks would not trigger runtime errors during concrete playback. +Kani generates warning messages for this.
  • +
  • This feature does not support generating unit tests for multiple assertion failures within the same harness. +This limitation might be removed in the future. +Kani generates warning messages for this.
  • +
  • This feature requires that you use the same Kani version to generate the test and to playback. +Any extra compilation option used during verification must be used during playback.
  • +
+

Application

+

You may be interested in applying Kani if you're in this situation:

+
    +
  1. You're working on a moderately important project in Rust.
  2. +
  3. You've already invested heavily in testing to ensure correctness.
  4. +
  5. You want to invest further, to gain a much higher degree of assurance.
  6. +
+
+

If you haven't already, we also recommend techniques like property testing and fuzzing (e.g. with bolero). +These yield good results, are very cheap to apply, and are often easy to adopt and debug.

+
+

In this section, we explain how Kani compares with other tools +and suggest where to start applying Kani in real code.

+

Comparison with other tools

+

Fuzzing (for example, with cargo-fuzz) is a unguided approach to random testing. +A fuzzer generally provides an input of random bytes, and then examines fairly generic properties (such as "doesn't crash" or "commit undefined behavior") about the resulting program.

+

Fuzzers generally get their power through a kind of evolutionary algorithm that rewards new mutant inputs that "discover" new branches of the program under test. +Fuzzers are excellent for testing security boundaries, precisely because they make no validity assumptions (hence, they are "unguided") when generating the input.

+

Property testing (for example, with Proptest) is a guided approach to random testing. +"Guided" in the sense that the test generally provides a strategy for generating random values that constrains their range. +The purpose of this strategy is to either focus on interesting values, or avoid failing assertions that only hold for a constrained set of inputs. +Tests in this style do actually state properties: For all inputs (of some constrained kind), this condition should hold.

+

Property testing is often quite effective, but the engine can't fully prove the property: It can only sample randomly a few of those values to test (though property testing libraries frequently give interesting "edge cases" a higher probability, making them more effective at bug-finding).

+

Model checking is similar to these techniques in how you use them, but it's non-random and exhaustive (though often only up to some bound on input or problem size). +Thus, properties checked with a model checker are effectively proofs. +Instead of naively trying all possible concrete inputs (which could be infeasible and blow up exponentially), model checkers like Kani will cleverly encode program traces as symbolic "SAT/SMT" problems, and hand them off to SAT/SMT solvers. +SAT/SMT solving is an NP-complete problem, but many practical programs can be model-checked within milliseconds to seconds (with notable exceptions: you can easily try to reverse a cryptographic hash with a model checker, but good luck getting it to terminate!)

+

Model checking allows you to prove non-trivial properties about programs, and check those proofs in roughly the same amount of time as a traditional test suite would take to run. +The downside is many types of properties can quickly become "too large" to practically model-check, and so writing "proof harnesses" (very similar to property tests and fuzzer harnesses) requires some skill to understand why the solver is not terminating and fix the structure of the problem you're giving it so that it does. +This process basically boils down to "debugging" the proof.

+

Looking for concurrency?

+

At present, Kani does not support verifying concurrent code. +Two tools of immediate interest are Loom and Shuttle. +Loom attempts to check all possible interleavings, while Shuttle chooses interleavings randomly. +The former is sound (like Kani), but the latter is more scalable to large problem spaces (like property testing).

+

Other tools

+

The Rust Formal Methods Interest Group maintains a list of interesting Rust verification tools.

+

Where to start on real code

+

It can be daunting to find the right place to start writing proofs for a real-world project. +This section will try to help you get over that hurdle.

+

In general, you're trying to do three things:

+
    +
  1. Find a place where it'd be valuable to have a proof.
  2. +
  3. Find a place where it won't be too difficult to prove something, just to start.
  4. +
  5. Figure out what a feasible longer-term goal might be.
  6. +
+

By far, the best strategy is to follow your testing. +Places where proof will be valuable are often places where you've written a lot of tests, because they're valuable there for the same reasons. +Likewise, code structure changes to make functions more unit-testable will also make functions more amenable to proof. +Often, by examining existing unit tests (and especially property tests), you can easily find a relatively self-contained function that's a good place to start.

+

Where is proof valuable?

+
    +
  1. +

    Where complicated things happen with untrusted user input. +These are often the critical "entry points" into the code. +These are also places where you probably want to try other techniques (e.g., fuzz testing).

    +
  2. +
  3. +

    Where unsafe is used extensively. +These are often places where you'll already have concentrated a lot of tests.

    +
  4. +
  5. +

    Where you have a complicated implementation that accomplishes a much simpler abstract problem. +Ideal places for property testing, if you haven't tried that already. +But the usual style of property tests you often write here (generate large random lists of operations, then compare between concrete and abstract model) won't be practical to directly port to model checking.

    +
  6. +
  7. +

    Where normal testing "smells" intractable.

    +
  8. +
+

Where is it easier to start?

+
    +
  1. +

    Find crates or files with smaller lists of dependencies. +Dependencies can sometimes blow up the tractability of proofs. +This can usually be handled, but requires a lot more investment to make it happen, and so isn't a good place to start.

    +
  2. +
  3. +

    Don't forget to consider starting with your dependencies. +Sometimes the best place to start won't be your code, but the code that you depend on. +If it's used by more projects that just yours, it will be valuable to more people, too!

    +
  4. +
  5. +

    Find well-tested code. +When you make changes to improve the unit-testability of code, that also makes it more amenable to proof, too.

    +
  6. +
+

Here are some things to avoid, when starting out:

+
    +
  1. +

    Lots of loops, or at least nested loops. +As we saw in the tutorial, right now we often need to put upper bounds on loops to make more limited claims.

    +
  2. +
  3. +

    Inductive data structures. +These are data structures with unbounded size (e.g., linked lists or trees.) +These can be hard to model since you need to set bounds on their size, similar to what happens with loops.

    +
  4. +
  5. +

    Input/Output code. +Kani doesn't model I/O, so if your code depends on behavior like reading/writing to a file, you won't be able to prove anything. +This is one obvious area where testability helps provability: often we separate I/O and "pure" computation into different functions, so we can unit-test the latter.

    +
  6. +
  7. +

    Deeper call graphs. +Functions that call a lot of other functions can require more investment to make tractable. +They may not be a good starting point.

    +
  8. +
  9. +

    Significant global state. +Rust tends to discourage this, but it still exists in some forms.

    +
  10. +
+

Your first proof

+

A first proof will likely start in the following form:

+
    +
  1. Nondeterministically initialize variables that will correspond to function inputs, with as few constraints as possible.
  2. +
  3. Call the function in question with these inputs.
  4. +
  5. Don't (yet) assert any post-conditions.
  6. +
+

Running Kani on this simple starting point will help figure out:

+
    +
  1. What unexpected constraints might be needed on your inputs (using kani::assume) to avoid "expected" failures.
  2. +
  3. Whether you're over-constrained. Check the coverage report using --coverage -Z source-coverage. Ideally you'd see 100% coverage, and if not, it's usually because you've assumed too much (thus over-constraining the inputs).
  4. +
  5. Whether Kani will support all the Rust features involved.
  6. +
  7. Whether you've started with a tractable problem. +(Remember to try setting #[kani::unwind(1)] to force early termination and work up from there.)
  8. +
+

Once you've got something working, the next step is to prove more interesting properties than just what Kani covers by default. +You accomplish this by adding new assertions (not just in your harness, but also to the code being run). +Even if a proof harness has no post-conditions being asserted directly, the assertions encountered along the way can be meaningful proof results by themselves.

+

Examples of the use of Kani

+

On the Kani blog, we've documented worked examples of applying Kani:

+
    +
  1. The Rectangle example of the Rust Book
  2. +
  3. A Rust standard library CVE
  4. +
  5. Verifying a part of Firecracker
  6. +
+

Developer documentation

+

Kani is an open source project open to external contributions.

+

The easiest way to contribute is to report any +issue you encounter +while using the tool. If you want to contribute to its development, +we recommend looking into these issues.

+

In this chapter, we provide documentation that might be helpful for Kani +developers (including external contributors):

+
    +
  1. Coding conventions.
  2. +
  3. Useful command-line instructions for Kani/CBMC/Git.
  4. +
  5. Development setup recommendations for working with cbmc.
  6. +
  7. Development setup recommendations for working with rustc.
  8. +
  9. Guide for testing in Kani.
  10. +
  11. Transition to StableMIR.
  12. +
+
+

NOTE: The developer documentation is intended for Kani developers and not +users. At present, the project is under heavy development and some items +discussed in this documentation may stop working without notice (e.g., commands +or configurations). Therefore, we recommend users to not rely on them.

+
+

Coding conventions

+

Formatting

+

We automate most of our formatting preferences. Our CI will run format checkers for PRs and pushes. +These checks are required for merging any PR.

+

For Rust, we use rustfmt +which is configured via the rustfmt.toml file. +We are also in the process of enabling clippy. +Because of that, we still have a couple of lints disabled (see .cargo/config for the updated list).

+

We also have a bit of C and Python code in our repository. +For C we use clang-format and for Python scripts we use autopep8. +See .clang-format +and pyproject.toml +for their configuration.

+

Exceptions

+

We recognize that in some cases, the formatting and lints automation may not be applicable to a specific code. +In those cases, we usually prefer explicitly allowing exceptions by locally disabling the check. +E.g., use #[allow] annotation instead of disabling a link on a crate or project level.

+ +

All source code files begin with a copyright and license notice. If this is a new file, please add the following notice:

+
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+

When modifying a file from another project, please keep their headers as is and append the following notice after them:

+
// ... existing licensing headers ...
+
+// Modifications Copyright Kani Contributors
+// See GitHub history for details.
+
+

Note: The comment escape characters will depend on the type of file you are working with. E.g.: For rust start the +header with //, but for python start with #.

+

We also have automated checks for the copyright notice. +There are a few file types where this rule doesn't apply. +You can see that list in the copyright-exclude file.

+

Code for soundness

+

We are developing Kani to provide assurance that critical Rust components are verifiably free of certain classes of +security and correctness issues. +Thus, it is critical that we provide a verification tool that is sound. +For the class of errors that Kani can verify, we should not produce a "No Error" result if there was in fact an +error in the code being verified, i.e., it has no +"False Negatives".

+

Because of that, we bias on the side of correctness. +Any incorrect modeling +that may trigger an unsound analysis that cannot be fixed in the short term should be mitigated. +Here are a few ways how we do that.

+

Compilation errors

+

Make sure to add user-friendly errors for constructs that we can't handle. +For example, Kani cannot handle the panic unwind strategy, and it will fail compilation if the crate uses this +configuration.

+

In general, it's preferred that error messages follow these guidelines used for rustc development. +If the errors are being emitted from kani-compiler, you should use the compiler error message utilities (e.g., the Session::span_err method). However, if the +errors are being emitted from kani-driver, you should use the functions provided in the util module in kani-driver.

+

Internal compiler errors

+

Even though this doesn't provide users the best experience, you are encouraged to add checks in the compiler for any +assumptions you make during development. +Those checks can be on the form of assert!() or unreachable!() +statement. +Please provide a meaningful message to help user understand why something failed, and try to explain, at least with +a comment, why this is the case.

+

We don't formally use any specific formal representation of function contract, +but whenever possible we do instrument the code with assertions that may represent the function pre- and +post-conditions to ensure we are modeling the user code correctly.

+

Verification errors

+

In cases where Kani fails to model a certain instruction or local construct that doesn't have a global effect, +we encode this failure as a verification error. +I.e., we generate an assertion failure instead of the construct we are modeling using +codegen_unimplemented(), +which blocks the execution whenever this construct is reached.

+

This will allow users to verify their crate successfully as long as +that construct is not reachable in any harness. If a harness has at least one possible execution path that reaches +such construct, Kani will fail the verification, and it will mark all checks, other than failed checks, with +UNDETERMINED status.

+

Create detailed issues for "TODO" tasks

+

It is OK to add "TODO" comments as long as they don't compromise user experience or the tool correctness. +When doing so, please create an issue that captures the task. +Add details about the task at hand including any impact to the user. +Finally, add the link to the issue that captures the "TODO" task as part of your comment.

+

E.g.:

+
// TODO: This function assumes type cannot be ZST. Check if that's always the case.
+// https://github.com/model-checking/kani/issues/XXXX
+assert!(!typ.is_zst(), "Unexpected ZST type");
+
+

Performant but readable

+

We aim at writing code that is performant but also readable and easy to maintain. +Avoid compromising the code quality if the performance gain is not significant.

+

Here are few tips that can help the readability of your code:

+
    +
  • Sort match arms, enum variants, and struct fields alphabetically.
  • +
  • Prefer concise but meaningful names.
  • +
  • Prefer exhaustive matches.
  • +
  • Prefer declarative over imperative programming.
  • +
+

Working with CBMC

+

This section describes how to access more advanced CBMC options from Kani.

+

CBMC arguments

+

Kani is able to handle common CBMC arguments as if they were its own (e.g., +--default-unwind <n>), but sometimes it may be necessary to use CBMC arguments which +are not handled by Kani.

+

To pass additional arguments for CBMC, you pass --cbmc-args to Kani. Note that +this "switches modes" from Kani arguments to CBMC arguments: Any arguments that +appear after --cbmc-args are considered to be CBMC arguments, so all Kani +arguments must be placed before it.

+

Thus, the command line format to invoke cargo kani with CBMC arguments is:

+
cargo kani [<kani-args>]* --cbmc-args [<cbmc-args>]*
+
+
+

NOTE: In cases where CBMC is not expected to emit a verification output, +you have to use Kani's argument --output-format old to turn off the +post-processing of output from CBMC.

+
+

Individual loop bounds

+

Setting --default-unwind <n> affects every loop in a harness. +Once you know a particular loop is causing trouble, sometimes it can be helpful to provide a specific bound for it.

+

In the general case, specifying just the highest bound globally for all loops +shouldn't cause any problems, except that the solver may take more time because +all loops will be unwound to the specified bound.

+

In situations where you need to optimize for the solver, individual bounds for +each loop can be provided on the command line. To do so, we first need to know +the labels assigned to each loop with the CBMC argument --show-loops:

+
# kani src/lib.rs --output-format old --cbmc-args --show-loops
+[...]
+Loop _RNvCs6JP7pnlEvdt_3lib17initialize_prefix.0:
+  file ./src/lib.rs line 11 column 5 function initialize_prefix
+
+Loop _RNvMs8_NtNtCswN0xKFrR8r_4core3ops5rangeINtB5_14RangeInclusivejE8is_emptyCs6JP7pnlEvdt_3lib.0:
+  file $RUST/library/core/src/ops/range.rs line 540 column 9 function std::ops::RangeInclusive::<Idx>::is_empty
+
+Loop gen-repeat<[u8; 10]::16806744624734428132>.0:
+
+

This command shows us the labels of the loops involved. Note that, as mentioned +in CBMC arguments, we need to use --output-format old to +avoid post-processing the output from CBMC.

+
+

NOTE: At the moment, these labels are constructed using the mangled name +of the function and an index. Mangled names are likely to change across +different versions, so this method is highly unstable.

+
+

Then, we can use the CBMC argument --unwindset label_1:bound_1,label_2:bound_2,... to specify an individual bound for each +loop as follows:

+
kani src/lib.rs --cbmc-args --unwindset _RNvCs6JP7pnlEvdt_3lib17initialize_prefix.0:12
+
+

Working with rustc

+

Kani is developed on the top of the Rust compiler, which is not distributed on crates.io and depends on +bootstrapping mechanisms to properly build its components. +Thus, our dependency on rustc crates are not declared in our Cargo.toml.

+

Below are a few hacks that will make it easier to develop on the top of rustc.

+

Code analysis for rustc definitions

+

IDEs rely on cargo to find dependencies and sources to provide proper code analysis and code completion. +In order to get these features working for rustc crates, you can do the following:

+

VSCode

+

Add the following to the rust-analyzer extension settings in settings.json:

+
    "rust-analyzer.rustc.source": "discover",
+    "rust-analyzer.workspace.symbol.search.scope": "workspace_and_dependencies",
+
+

Ensure that any packages that use rustc data structures have the following line set in their Cargo.toml

+
[package.metadata.rust-analyzer]
+# This package uses rustc crates.
+rustc_private=true
+
+

You may also need to install the rustc-dev package using rustup

+
rustup toolchain install nightly --component rustc-dev
+
+

Debugging in VS code

+

To debug Kani in VS code, first install the CodeLLDB extension. +Then add the following lines at the start of the main function (see the CodeLLDB manual for details):

+
{
+    let url = format!(
+        "vscode://vadimcn.vscode-lldb/launch/config?{{'request':'attach','sourceLanguages':['rust'],'waitFor':true,'pid':{}}}",
+        std::process::id()
+    );
+    std::process::Command::new("code").arg("--open-url").arg(url).output().unwrap();
+}
+
+

Note that pretty printing for the Rust nightly toolchain (which Kani uses) is not very good as of June 2022. +For example, a vector may be displayed as vec![{...}, {...}] on nightly Rust, when it would be displayed as vec![Some(0), None] on stable Rust. +Hopefully, this will be fixed soon.

+

RustRover / CLion

+

This is not a great solution, but it works for now (see https://github.com/intellij-rust/intellij-rust/issues/1618 +for more details).

+

Open the Cargo.toml of your crate (e.g.: kani-compiler), and do the following:

+
    +
  1. Add optional dependencies on the rustc crates you are using.
  2. +
  3. Add a feature that enable those dependencies.
  4. +
  5. Toggle that feature using the IDE GUI.
  6. +
+

Here is an example:

+
# ** At the bottom of the dependencies section: **
+# Adjust the path here to point to a local copy of the rust compiler.
+# E.g.: ~/.rustup/toolchains/<toolchain>/lib/rustlib/rustc-src/rust/compiler
+rustc_smir = { path = "<path_to_rustc>/rustc_smir", optional = true }
+stable_mir = { path = "<path_to_rustc>/stable_mir", optional = true }
+
+[features]
+clion = ['rustc_smir', 'stable_mir']
+
+

Don't forget to rollback the changes before you create your PR.

+

EMACS (with use-package)

+

First, Cargo.toml and rustup toolchain steps are identical to VS +Code. Install Rust-analyzer binary under ~/.cargo/bin/.

+

On EMACS, add the following to your EMACS lisp files. They will +install the necessary packages using the use-package manager.

+
;; Install LSP
+(use-package lsp-mode
+  :commands lsp)
+(use-package lsp-ui)
+
+;; Install Rust mode
+(use-package toml-mode)
+(use-package rust-mode)
+
+(setq lsp-rust-server 'rust-analyzer)
+(setenv "PATH" (concat (getenv "PATH") ":/home/USER/.cargo/bin/"))
+
+

If EMACS complains that it cannot find certain packages, try running +M-x package-refresh-contents.

+

For LSP to be able to find rustc_private files used by Kani, you +will need to modify variable lsp-rust-analyzer-rustc-source. Run +M-x customize-variable, type in lsp-rust-analyzer-rustc-source, +click Value Menu and change it to Path. Paste in the path to +Cargo.toml of rustc's source code. You can find the source code +under .rustup, and the path should end with +compiler/rustc/Cargo.toml. Important: make sure that this +rustc is the same version and architecture as what Kani uses. If +not, LSP features like definition lookup may be break.

+

This ends the basic install for EMACS. You can test your configuration +with the following steps.

+
    +
  1. Opening up a rust file with at least one rustc_private import.
  2. +
  3. Activate LSP mode with M-x lsp.
  4. +
  5. When asked about the root of the project, pick one of them. Make +sure that whichever root you pick has a Cargo.toml with +rustc_private=true added.
  6. +
  7. If LSP asks if you want to watch all files, select yes. For less +powerful machines, you may want to adjust that later.
  8. +
  9. On the file with rustc_private imports, do the following. If both +work, then you are set up. +
      +
    • Hover mouse over the rustc_private import. If LSP is working, +you should get information about the imported item.
    • +
    • With text cursor over the same rustc_private import, run M-x lsp-find-definition. This should jump to the definition within +rustc's source code.
    • +
    +
  10. +
+

LSP mode can integrate with flycheck for instant error checking and +company for auto-complete. Consider adding the following to the +configuration.

+
(use-package flycheck
+  :hook (prog-mode . flycheck-mode))
+
+(use-package company
+  :hook (prog-mode . company-mode)
+  :config
+   (global-company-mode))
+
+

clippy linter can be added by changing the LSP install to:

+
(use-package lsp-mode
+  :commands lsp
+  :custom
+  (lsp-rust-analyzer-cargo-watch-command "clippy"))
+
+

Finally lsp-mode can run rust-analyzer via TRAMP for remote +development. We found this way of using rust-analyzer to be unstable +as of 2022-06. If you want to give it a try you will need to add a +new LSP client for that remote with the following code.

+
(lsp-register-client
+  (make-lsp-client
+	:new-connection (lsp-tramp-connection "/full/path/to/remote/machines/rust-analyzer")
+	:major-modes '(rust-mode)
+	:remote? t
+	:server-id 'rust-analyzer-remote))
+
+

For further details, please see https://emacs-lsp.github.io/lsp-mode/page/remote/.

+

Custom rustc

+

There are a few reasons why you may want to use your own copy of rustc. E.g.:

+
    +
  • Enable more verbose logs.
  • +
  • Use a debug build to allow you to step through rustc code.
  • +
  • Test changes to rustc.
  • +
+

We will assume that you already have a Kani setup and that the variable KANI_WORKSPACE contains the path to your Kani workspace.

+

It's highly recommended that you start from the commit that corresponds to the current rustc version from your workspace. +To get that information, run the following command:

+
cd ${KANI_WORKSPACE} # Go to your Kani workspace.
+rustc --version # This will print the commit id. Something like:
+# rustc 1.60.0-nightly (0c292c966 2022-02-08)
+#                       ^^^^^^^^^ this is used as the ${COMMIT_ID} below
+# E.g.:
+COMMIT_ID=0c292c966
+
+

First you need to clone and build stage 2 of the compiler. +You should tweak the configuration to satisfy your use case. +For more details, see https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html and https://rustc-dev-guide.rust-lang.org/building/suggested.html.

+
git clone https://github.com/rust-lang/rust.git
+cd rust
+git checkout ${COMMIT_ID:?"Missing rustc commit id"}
+./configure --enable-extended --tools=src,rustfmt,cargo --enable-debug --set=llvm.download-ci-llvm=true
+./x.py build -i --stage 2
+
+

Now create a custom toolchain (here we name it custom-toolchain):

+
# Use x86_64-apple-darwin for MacOs
+rustup toolchain link custom-toolchain build/x86_64-unknown-linux-gnu/stage2
+cp build/x86_64-unknown-linux-gnu/stage2-tools-bin/* build/x86_64-unknown-linux-gnu/stage2/bin/
+
+

Finally, override the current toolchain in your kani workspace and rebuild kani:

+
cd ${KANI_WORKSPACE}
+rustup override set custom-toolchain
+cargo clean
+cargo build-dev
+
+

Rust compiler utilities to debug kani-compiler

+

Enable rustc logs

+

In order to enable logs, you can just define the RUSTC_LOG variable, as documented here: https://rustc-dev-guide.rust-lang.org/tracing.html.

+

Note that, depending on the level of logs you would like to get (debug and trace are not enabled by default), you'll need to build your own version of rustc as described above. +For logs that are related to kani-compiler code, use the KANI_LOG variable.

+

Debugging type layout

+

In order to print the type layout computed by the Rust compiler, you can pass the following flag to rustc: -Zprint-type-sizes. +This flag can be passed to kani or cargo kani by setting the RUSTFLAG environment variable.

+
RUSTFLAGS=-Zprint-type-sizes kani test.rs
+
+

When enabled, the compiler will print messages that look like:

+
print-type-size type: `std::option::Option<bool>`: 1 bytes, alignment: 1 bytes
+print-type-size     variant `Some`: 1 bytes
+print-type-size         field `.0`: 1 bytes
+print-type-size     variant `None`: 0 bytes
+
+

Inspecting the MIR

+

You can easily visualize the MIR that is used as an input to code generation by setting the Rust flag --emit mir. I.e.:

+
RUSTFLAGS=--emit=mir kani test.rs
+
+

The compiler will generate a few files, but we recommend looking at the files that have the following suffix: kani.mir. +Those files will include the entire MIR collected by our reachability analysis. +It will include functions from all dependencies, including the std library. +One limitation is that we dump one copy of each specialization of the MIR function, even though the MIR body itself doesn't change.

+

Transition to StableMIR

+

We have partnered with the Rust compiler team in the initiative to introduce stable +APIs to the compiler that can be used by third-party tools, which is known as the +Stable MIR Project, or just StableMIR. +This means that we are starting to use the new APIs introduced by this project as is, +despite them not being stable yet.

+

StableMIR APIs

+

For now, the StableMIR APIs are exposed as a crate in the compiler named stable_mir. +This crate includes the definition of structures and methods to be stabilized, +which are expected to become the stable APIs in the compiler. +To reduce the migration burden, these APIs are somewhat close to the original compiler interfaces. +However, some changes have been made to make these APIs cleaner and easier to use.

+

For example:

+
    +
  1. The usage of the compiler context (aka TyCtxt) is transparent to the user. +The StableMIR implementation caches this context in a thread local variable, +and retrieves it whenever necessary. +
      +
    • Because of that, code that uses the StableMIR has to be invoked inside a run call.
    • +
    +
  2. +
  3. The DefId has been specialized into multiple types, +making its usage less error prone. E.g.: +FnDef represents the definition of a function, +while StaticDef is the definition of a static variable. +
      +
    • Note that the same DefId may be mapped to different definitions according to its context. +For example, an InstanceDef and a FnDef may represent the same function definition.
    • +
    +
  4. +
  5. Methods that used to be exposed as part of TyCtxt are now part of a type. +Example, the function TyCtxt.instance_mir is now Instance::body.
  6. +
  7. There is no need for explicit instantiation (monomorphization) of items from anInstance::body. +This method already instantiates all types and resolves all constants before converting +it to stable APIs.
  8. +
+

Performance

+

Since the new APIs require converting internal data to a stable representation, +the APIs were also designed to avoid needless conversions, +and to allow extra information to be retrieved on demand.

+

For example, Ty is just an identifier, while TyKind is a structure that can be retrieved via Ty::kind method. +The TyKind is a more structured object, thus, +it is only generated when the kind method is invoked. +Since this translation is not cached, +many of the functions that the rust compiler used to expose in Ty, +is now only part of TyKind. +The reason being that there is no cache for the TyKind, +and users should do the caching themselves to avoid needless translations.

+

From our initial experiments with the transition of the reachability algorithm to use StableMIR, +there is a small penalty of using StableMIR over internal rust compiler APIs. +However, they are still fairly efficient and it did not impact the overall compilation time.

+

Interface with internal APIs

+

To reduce the burden of migrating to StableMIR, +and to allow StableMIR to be used together with internal APIs, +there are two helpful methods to convert StableMIR constructs to internal rustc and back:

+
    +
  • rustc_internal::internal(): Convert a Stable item into an internal one.
  • +
  • rustc_internal::stable(): Convert an internal item into a Stable one.
  • +
+

Both of these methods are inside rustc_smir crate in the rustc_internal +module inside the compiler. +Note that there is no plan to stabilize any of these methods, +and there's also no guarantee on its support and coverage.

+

The conversion is not implemented for all items, and some conversions may be incomplete. +Please proceed with caution when using these methods.

+

Besides that, do not invoke any other rustc_smir methods, except for run. +This crate's methods are not meant to be invoked externally. +Note that, the method run will also eventually be replaced by a Stable driver.

+

Creating and modifying StableMIR items

+

For now, StableMIR should only be used to get information from the compiler. +Do not try to create or modify items directly, as it may not work. +This may result in incorrect behavior or an internal compiler error (ICE).

+

Naming conventions in Kani

+

As we adopt StableMIR, we would like to introduce a few conventions to make it easier to maintain the code. +Whenever there is a name conflict, for example, Ty or codegen_ty, +use a suffix to indicate which API you are using. +Stable for StableMIR and Internal for rustc internal APIs.

+

A module should either default its naming to Stable APIs or Internal APIs. +I.e.: Modules that have been migrated to StableMIR don't need to add the Stable suffix to stable items. +While those that haven't been migrated, should add Stable, but no Internal is needed.

+

For example, the codegen::typ module will likely include methods:

+

codegen_ty(&mut self, Ty) and codegen_ty_stable(&mut, TyStable) to handle +internal and stable APIs.

+

Command cheat sheets

+

Development work in the Kani project depends on multiple tools. Regardless of +your familiarity with the project, the commands below may be useful for +development purposes.

+

Kani

+

Build

+
# Error "'rustc' panicked at 'failed to lookup `SourceFile` in new context'"
+# or similar error? Cleaning artifacts might help.
+# Otherwise, comment the line below.
+cargo clean
+cargo build-dev
+
+

Test

+
# Full regression suite
+./scripts/kani-regression.sh
+
+
# Delete regression test caches (Linux)
+rm -r build/x86_64-unknown-linux-gnu/tests/
+
+
# Delete regression test caches (macOS)
+rm -r build/x86_64-apple-darwin/tests/
+
+
# Test suite run (we can only run one at a time)
+# cargo run -p compiletest -- --suite ${suite} --mode ${mode}
+cargo run -p compiletest -- --suite kani --mode kani
+
+
# Build documentation
+cd docs
+./build-docs.sh
+
+

Debug

+

These can help understand what Kani is generating or encountering on an example or test file:

+
# Enable `debug!` macro logging output when running Kani:
+kani --debug file.rs
+
+
# Use KANI_LOG for a finer grain control of the source and verbosity of logs.
+# E.g.: The command below will print all logs from the kani_middle module.
+KANI_LOG="kani_compiler::kani_middle=trace" kani file.rs
+
+
# Keep CBMC Symbol Table and Goto-C output (.json and .goto)
+kani --keep-temps file.rs
+
+
# Generate "C code" from CBMC IR (.c)
+kani --gen-c file.rs
+
+
# Generate a ${INPUT}.kani.mir file with a human friendly MIR dump
+# for all items that are compiled to the respective goto-program.
+RUSTFLAGS="--emit mir" kani ${INPUT}.rs
+
+

The KANI_REACH_DEBUG environment variable can be used to debug Kani's reachability analysis. +If defined, Kani will generate a DOT graph ${INPUT}.dot with the graph traversed during reachability analysis. +If defined and not empty, the graph will be filtered to end at functions that contains the substring +from KANI_REACH_DEBUG.

+

Note that this will only work on debug builds.

+
# Generate a DOT graph ${INPUT}.dot with the graph traversed during reachability analysis
+KANI_REACH_DEBUG= kani ${INPUT}.rs
+
+# Generate a DOT graph ${INPUT}.dot with the sub-graph traversed during the reachability analysis
+# that connect to the given target.
+KANI_REACH_DEBUG="${TARGET_ITEM}" kani ${INPUT}.rs
+
+

CBMC

+
# See CBMC IR from a C file:
+goto-cc file.c -o file.out
+goto-instrument --print-internal-representation file.out
+# or (for json symbol table)
+cbmc --show-symbol-table --json-ui file.out
+# or (an alternative concise format)
+cbmc --show-goto-functions file.out
+
+
# Recover C from goto-c binary
+goto-instrument --dump-c file.out > file.gen.c
+
+

Git

+

The Kani project follows the squash and merge option for pull request merges. +As a result:

+
    +
  1. The title of your pull request will become the main commit message.
  2. +
  3. The messages from commits in your pull request will appear by default as a bulleted list in the main commit message body.
  4. +
+

But the main commit message body is editable at merge time, so you don't have to worry about "typo fix" messages because these can be removed before merging.

+
# Set up your git fork
+git remote add fork git@github.com:${USER}/kani.git
+
+
# Reset everything. Don't have any uncommitted changes!
+git clean -xffd
+git submodule foreach --recursive git clean -xffd
+git submodule update --init
+
+
# Need to update local branch (e.g. for an open pull request?)
+git fetch origin
+git merge origin/main
+# Or rebase, but that requires a force push,
+# and because we squash and merge, an extra merge commit in a PR doesn't hurt.
+
+
# Checkout a pull request locally without the github cli
+git fetch origin pull/$ID/head:pr/$ID
+git switch pr/$ID
+
+
# Push to someone else's pull request
+git origin add $USER $GIR_URL_FOR_THAT_USER
+git push $USER $LOCAL_BRANCH:$THEIR_PR_BRANCH_NAME
+
+
# Search only git-tracked files
+git grep codegen_panic
+
+
# Accidentally commit to main?
+# "Move" commit to a branch:
+git checkout -b my_branch
+# Fix main:
+git branch --force main origin/main
+
+

cargo kani assess

+

Assess is an experimental new feature to gather data about Rust crates, to aid the start of proof writing.

+

In the short-term, assess collects and dumps tables of data that may help Kani developers understand what's needed to begin writing proofs for another project. +For instance, assess may help answer questions like:

+
    +
  1. Does Kani successfully build all of the crates involved in this project? If not, why not?
  2. +
  3. Does Kani support all the Rust language features necessary to do verification with this project? If not, which are most important?
  4. +
+

In the long-term, assess will become a user-facing feature, and help Kani users get started writing proofs. +We expect that users will have the same questions as above, but in the long term, hopefully the answers to those trend towards an uninteresting "yes." +So the new questions might be:

+
    +
  1. Is this project ready for verification? Projects need to be reasonably well-tested first. +Our operating hypothesis is that code currently covered by unit tests is the code that could become covered by proofs.
  2. +
  3. How much of given project (consisting of multiple packages or workspaces) or which of the user's projects might be verifiable? +If a user wants to start trying Kani, but they have the choice of several different packages where they might try, we can help find the package with the lowest hanging fruit.
  4. +
  5. Given a package, where in that package's code should the user look, in order to write the first (or next) proof?
  6. +
+

These long-term goals are only "hinted at" with the present experimental version of assess. +Currently, we only get as far as finding out which tests successfully verify (concretely) with Kani. +This might indicate tests that could be generalized and converted into proofs, but we currently don't do anything to group, rank, or otherwise heuristically prioritize what might be most "interesting." +(For instance, we'd like to eventually compute coverage information and use that to help rank the results.) +As a consequence, the output of the tool is very hard to interpret, and likely not (yet!) helpful to new or potential Kani users.

+

Using Assess

+

To assess a package, run:

+
cargo kani --enable-unstable assess
+
+

As a temporary hack (arguments shouldn't work like this), to assess a single cargo workspace, run:

+
cargo kani --enable-unstable --workspace assess
+
+

To scan a collection of workspaces or packages that are not part of a shared workspace, run:

+
cargo kani --enable-unstable assess scan
+
+

The only difference between 'scan' and 'regular' assess is how the packages built are located. +All versions of assess produce the same output and metrics. +Assess will normally build just like cargo kani or cargo build, whereas scan will find all cargo packages beneath the current directory, even in unrelated workspaces. +Thus, 'scan' may be helpful in the case where the user has a choice of packages and is looking for the easiest to get started with (in addition to the Kani developer use-case, of aggregating statistics across many packages).

+

(Tip: Assess may need to run for awhile, so try using screen, tmux or nohup to avoid terminating the process if, for example, an ssh connection breaks. +Some tests can also consume huge amounts of ram when run through Kani, so you may wish to use ulimit -v 6000000 to prevent any processes from using more than 6GB. +You can also limit the number of concurrent tests that will be run by providing e.g. -j 4, currently as a prepended argument, like --enable-unstable or --workspace in the examples above.)

+

What assess does

+

Assess builds all the packages requested in "test mode" (i.e. --tests), and runs all the same tests that cargo test would, except through Kani. +This gives end-to-end assurance we're able to actually build and run code from these packages, skipping nothing of what the verification process would need, except that the harnesses don't have any nondeterminism (kani::any()) and consequently don't "prove" much. +The interesting signal comes from what tests cannot be analyzed by Kani due to unsupported features, performance problems, crash bugs, or other issues that get in the way.

+

Currently, assess forces termination by using unwind(1) on all tests, so many tests will fail with unwinding assertions.

+

Current Assess Results

+

Assess produces a few tables of output (both visually in the terminal, and in a more detailed json format) so far:

+

Unsupported features

+
======================================================
+ Unsupported feature           |   Crates | Instances
+                               | impacted |    of use
+-------------------------------+----------+-----------
+ caller_location               |       71 |       239
+ simd_bitmask                  |       39 |       160
+...
+
+

The unsupported features table aggregates information about features that Kani does not yet support. +These correspond to uses of codegen_unimplemented in the kani-compiler, and appear as warnings during compilation.

+

Unimplemented features are not necessarily actually hit by (dynamically) reachable code, so an immediate future improvement on this table would be to count the features actually hit by failing test cases, instead of just those features reported as existing in code by the compiler. +In other words, the current unsupported features table is not what we want to see, in order to perfectly prioritize implementing these features, because we may be counting features that no proof would ever hit. +A perfect signal here isn't possible: there may be code that looks statically reachable, but is never dynamically reachable, and we can't tell. +But we can use test coverage as an approximation: well-tested code will hopefully cover most of the dynamically reachable code. +The operating hypothesis of assess is that code covered by tests is code that could be covered by proof, and so measuring unsupported features by those actually hit by a test should provide a better "signal" about priorities. +Implicitly deprioritizing unsupported features because they aren't covered by tests may not be a bug, but a feature: we may simply not want to prove anything about that code, if it hasn't been tested first, and so adding support for that feature may not be important.

+

A few notes on terminology:

+
    +
  1. "Crates impacted" here means "packages in the current workspace (or scan) where the building of that package (and all of its dependencies) ultimately resulted in this warning." +For example, if only assessing a single package (not a workspace) this could only be 1 in this column, regardless of the number of dependencies.
  2. +
  3. "Instances of use" likewise means "total instances found while compiling this package's tests and all the (reachable) code in its dependencies."
  4. +
  5. These counts are influenced by (static) reachability: if code is not potentially reachable from a test somehow, it will not be built and will not be counted.
  6. +
+

Test failure reasons

+
================================================
+ Reason for failure           | Number of tests
+------------------------------+-----------------
+ unwind                       |              61
+ none (success)               |               6
+ assertion + overflow         |               2
+...
+
+

The test failure reasons table indicates why, when assess ran a test through Kani, it failed to verify. +Notably:

+
    +
  1. Because we force termination with unwind(1), we expect unwind to rank highly.
  2. +
  3. We do report number of tests succeeding on this table, to aid understanding how well things went overall.
  4. +
  5. The reported reason is the "property class" of the CBMC property that failed. So assertion means an ordinary assert!() was hit (or something else with this property class).
  6. +
  7. When multiple properties fail, they are aggregated with +, such as assertion + overflow.
  8. +
  9. Currently this table does not properly account for should_fail tests, so assertion may actually be "success": the test should hit an assertion and did.
  10. +
+

Promising test cases

+
=============================================================================
+ Candidate for proof harness                           | Location
+-------------------------------------------------------+---------------------
+ float::tests::f64_edge_cases                          | src/float.rs:226
+ float::tests::f32_edge_cases                          | src/float.rs:184
+ integer::tests::test_integers                         | src/integer.rs:171
+
+

This table is the most rudimentary so far, but is the core of what long-term assess will help accomplish. +Currently, this table just presents (with paths displayed in a clickable manner) the tests that successfully "verify" with Kani. +These might be good candidates for turning into proof harnesses. +This list is presently unordered; the next step for improving it would be to find even a rudimentary way of ranking these test cases (e.g. perhaps by code coverage).

+

How Assess Works

+

kani-compiler emits *.kani-metadata.json for each target it builds. +This format can be found in the kani_metadata crate, shared by kani-compiler and kani-driver. +This is the starting point for assess.

+

Assess obtains this metadata by essentially running a cargo kani:

+
    +
  1. With --all-features turned on
  2. +
  3. With unwind always set to 1
  4. +
  5. In test mode, i.e. --tests
  6. +
  7. With test-case reachability mode. Normally Kani looks for proof harnesses and builds only those. Here we switch to building only the test harnesses instead.
  8. +
+

Assess starts by getting all the information from these metadata files. +This is enough by itself to construct a rudimentary "unsupported features" table. +But assess also uses it to discover all the test cases, and (instead of running proof harnesses) it then runs all these test harnesses under Kani.

+

Assess produces a second metadata format, called (unsurprisingly) "assess metadata". +(Found in kani-driver under src/assess/metadata.rs.) +This format records the results of what assess does.

+

This metadata can be written to a json file by providing --emit-metadata <file> to assess. +Likewise, scan can be told to write out this data with the same option.

+

Assess metadata is an aggregatable format. +It does not apply to just one package, as assess can work on a workspace of packages. +Likewise, scan uses and produces the exact same format, across multiple workspaces.

+

So far all assess metadata comes in the form of "tables" which are built with TableBuilder<T: TableRow>. +This is documented further in src/assess/table_builder.rs.

+

Using Assess on the top-100 crates

+

There is a script in the Kani repo for this purpose.

+

This will clone the top-100 crates to /tmp/top-100-experiment and run assess scan on them:

+
./scripts/exps/assess-scan-on-repos.sh
+
+

If you'd like to preseve the results, you can direct scan to use a different directory with an environment variable:

+
ASSESS_SCAN="~/top-100-experiment" ./scripts/exps/assess-scan-on-repos.sh
+
+

To re-run the experiment, it suffices to be in the experiment directory:

+
cd ~/top-100-experiment && ~/kani/scripts/exps/assess-scan-on-repos.sh
+
+

Testing

+

Testing in Kani is carried out in multiple ways. There are at least +two very good reasons to do it:

+
    +
  1. +

    Software regression: A regression is a type of bug +that appears after a change is introduced where a feature that +was previously working has unexpectedly stopped working.

    +

    Regression testing allows one to prevent a software regression +from happening by running a comprehensive set of working tests +before any change is committed to the project.

    +
  2. +
  3. +

    Software metrics: A metric is a measure of software +characteristics which are quantitative and countable. Metrics are +particularly valuable for project management purposes.

    +
  4. +
+

We recommend reading our section on Regression +Testing if you're interested in Kani +development. To run kani on a large number of remotely +hosted crates, please see Repository Crawl.

+

Regression testing

+

Kani relies on a quite extensive range of tests to perform regression testing. +Regression testing can be executed by running the command:

+
./scripts/kani-regression.sh
+
+

The kani-regression.sh script executes different testing commands, which we classify into:

+ +

See below for a description of each one.

+

Note that regression testing is run whenever a Pull Request is opened, updated or merged +into the main branch. Therefore, it's a good idea to run regression testing locally before +submitting a Pull Request for Kani.

+

Kani testing suites

+

The Kani testing suites are the main testing resource for Kani. In most cases, the +tests contained in the Kani testing suites are single Rust files that are run +using the following command:

+
kani file.rs <options>
+
+

Command-line options can be passed to the test by adding a special +comment to the file. See testing options for more details.

+

In particular, the Kani testing suites are composed of:

+
    +
  • kani: The main testing suite for Kani. The test is a single Rust file that's +run through Kani. In general, the test passes if verification with Kani +is successful, otherwise it fails.
  • +
  • firecracker: Works like kani but contains tests inspired by +Firecracker code.
  • +
  • prusti: Works like kani but contains tests from the +Prusti tool.
  • +
  • smack: Works like kani but contains tests from the +SMACK tool.
  • +
  • kani-fixme: Similar to kani, but runs ignored tests from the kani testing +suite (i.e., tests with fixme or ignore in their name). +Allows us to detect when a previously not supported test becomes +supported. More details in "Fixme" tests.
  • +
  • expected: Similar to kani but with an additional check which ensures that +lines appearing in *.expected files appear in the output +generated by kani.
  • +
  • ui: Works like expected, but focuses on the user interface (e.g., +warnings) instead of the verification output.
  • +
  • cargo-kani: This suite is designed to test the cargo-kani command. As such, +this suite works with packages instead of single Rust files. +Arguments can be specified in the Cargo.toml configuration file. +Similar to the expected suite, we look for *.expected files +for each harness in the package.
  • +
  • cargo-ui: Similar to cargo-kani, but focuses on the user interface like the ui test suite.
  • +
  • script-based-pre: This suite is useful to execute script-based tests, and +also allows checking expected output and exit codes after +running them. The suite uses the exec mode, described in +more detail here.
  • +
+

We've extended +compiletest (the +Rust compiler testing framework) to work with these suites. That way, we take +advantage of all compiletest features (e.g., parallel execution).

+

Testing stages

+

The process of running single-file tests is split into three stages:

+
    +
  • check: This stage uses the Rust front-end to detect if the example is valid +Rust code.
  • +
  • codegen: This stage uses the Kani back-end to determine if we can generate +GotoC code.
  • +
  • verify: This stage uses CBMC to obtain a verification result.
  • +
+

If a test fails, the error message will include the stage where it failed:

+
error: test failed: expected check success, got failure
+
+

When working on a test that's expected to fail, there are two options to +indicate an expected failure. The first one is to add a comment

+
// kani-<stage>-fail
+
+

at the top of the test file, where <stage> is the stage where the test is +expected to fail.

+

The other option is to use the predicate kani::expect_fail(cond, message) +included in the Kani library. The cond in kani::expect_fail is a condition +that you expect not to hold during verification. The testing framework expects +one EXPECTED FAIL message in the verification output for each use of the +predicate.

+
+

NOTE: kani::expect_fail is only useful to indicate failure in the +verify stage, errors in other stages will be considered testing failures.

+
+

Testing options

+

Many tests will require passing command-line options to Kani. These options can +be specified in single Rust files by adding a comment at the top of the file:

+
// kani-flags: <options>
+
+

For example, to use an unwinding value of 4 in a test, we can write:

+
// kani-flags: --default-unwind 4
+
+

For cargo-kani tests, the preferred way to pass command-line options is adding +them to Cargo.toml. See Usage on a package for more details.

+

"Fixme" tests

+

Any test containing fixme or ignore in its name is considered a test not +supported for some reason (i.e., they return an unexpected verification result).

+

However, "fixme" tests included in the kani folder are run via the kani-fixme +testing suite. kani-fixme works on test files from kani but:

+
    +
  1. Only runs tests whose name contains fixme or ignore (ignoring the rest).
  2. +
  3. The expected outcome is failure. In other words, a test is successful if it +fails.
  4. +
+

We welcome contributions with "fixme" tests which demonstrate a bug or +unsupported feature in Kani. Ideally, the test should include some comments +regarding:

+
    +
  • The expected result of the test.
  • +
  • The actual result of the test (e.g., interesting parts of the output).
  • +
  • Links to related issues.
  • +
+

To include a new "fixme" test in kani you only need to ensure its name contains +fixme or ignore. If your changes to Kani cause a "fixme" test to become +supported, you only need to rename it so the name does not contain fixme nor +ignore.

+

Rust unit tests

+

These tests follow the +Rust unit testing +style.

+

At present, Kani runs unit tests from the following packages:

+
    +
  • cprover_bindings
  • +
  • kani-compiler
  • +
  • cargo-kani
  • +
+

Python unit tests

+

We use the Python unit testing framework to +test the CBMC JSON parser.

+

Script-based tests

+

These are tests which are run using scripts. Scripting gives us the ability to +perform ad-hoc checks that cannot be done otherwise. They are currently used +for:

+
    +
  • Standard library codegen
  • +
  • Firecracker virtio codegen
  • +
  • Diamond dependency
  • +
+

In fact, most of them are equivalent to running cargo kani and performing +checks on the output. The downside to scripting is that these tests will always +be run, even if there have not been any changes since the last time the +regression was run.

+
+

NOTE: With the addition of the exec mode for compiletest (described +below), we'll be migrating these script-based tests to other suites using the +exec mode. The exec mode allows us to take advantage of compiletest +features while executing script-based tests (e.g., parallel execution).

+
+

The exec mode

+

The exec mode in compiletest allows us to execute script-based tests, in +addition to checking expected output and exit codes after running them.

+

In particular, tests are expected to be placed directly under the test directory +(e.g., script-based-pre) in a directory with a config.yml file, which +should contain:

+
    +
  • script: The path to the script to be executed.
  • +
  • expected (optional): The path to the .expected file to +use for output comparison.
  • +
  • exit_code (optional): The exit code to be returned by executing +the script (a zero exit code is expected if not specified).
  • +
+

For example, let's say want to test the script exit-one.sh:

+
echo "Exiting with code 1!"
+exit 1
+
+

In this case, we'll create a folder that contains the config.yml file:

+
script: exit-one.sh
+expected: exit-one.expected
+exit_code: 1
+
+

where exit-one.expected is simply a text file such as:

+
Exiting with code 1!
+
+

If expected isn't specified, the output won't be checked. If exit_code isn't +specified, the exec mode will check the exit code was zero.

+

Note that all paths specified in the config.yml file are local to the test +directory, which is the working directory assumed when executing the test. This +is meant to avoid problems when executing the test manually.

+

(Experimental) Testing with a Large Number of Repositories

+

This section explains how to run Kani on a large number of crates +downloaded from git forges. You may want to do this if you are going +to test Kani's ability to handle Rust features found in projects out +in the wild.

+

For the first half, we will explain how to use data from crates.io to +pick targets. Second half will explain how to use a script to run on a +list of selected repositories.

+

Picking Repositories

+

In picking repositories, you may want to select by metrics like +popularity or by the presence of certain features. In this section, we +will explain how to select top ripostes by download count.

+

We will use the db-dump method of getting data from crates.io as it +is zero cost to their website and gives us SQL access. To start, have +the following programs set up on your computer.

+
    +
  • docker
  • +
  • docker-compose.
  • +
+
    +
  1. Start PostgreSQL. Paste in the following yaml file as +docker-compose.yaml. version: '3.3' may need to change.
  2. +
+
version: '3.3'
+services:
+  db:
+    image: postgres:latest
+    restart: always
+    environment:
+      - POSTGRES_USER=postgres
+      - POSTGRES_PASSWORD=postgres
+    volumes:
+      - crates-data:/var/lib/postgresql/data
+    logging:
+      driver: "json-file"
+      options:
+        max-size: "50m"
+volumes:
+  crates-data:
+    driver: local
+
+

Then, run the following to start the setup.

+
docker-compose up -d
+
+

Once set up, run docker ls to figure out the container's name. We +will refer to the name as $CONTAINER_NAME from now on.

+
    +
  1. +

    Download actual data from crates.io. First, run the following +command to get a shell in the container: docker exec -it --user postgres $CONTAINER_NAME bash. Now, run the following to grab and +install the data into the repository. Please note that this may +take a while.

    +
    wget https://static.crates.io/db-dump.tar.gz
    +tar -xf db-dump.tar.gz
    +psql postgres -f */schema.sql
    +psql postgres -f */import.sql
    +
    +
  2. +
  3. +

    Extract the data. In the same docker shell, run the following to +extract the top 1k repositories. Other SQL queries may be used if +you want another criteria

    +
    \copy
    +(SELECT name, repository, downloads  FROM crates
    +WHERE repository LIKE 'http%' ORDER BY DOWNLOADS DESC LIMIT 1000)
    +to 'top-1k.csv' csv header;
    +
    +
  4. +
  5. +

    Clean the data. The above query will capture duplicates paths that +are deeper than the repository. You can clean these out.

    +
      +
    • URL from CSV: cat top-1k.csv | awk -F ',' '{ print $2 }' | grep -v 'http.*'
    • +
    • Remove long paths: sed 's/tree\/master.*$//g'
    • +
    • Once processed, you can dedup with sort | uniq --unique
    • +
    +
  6. +
+

Running the List of Repositories

+

In this step we will download the list of repositories using a script +assess-scan-on-repos.sh

+

Make sure to have Kani ready to run. For that, see the build instructions.

+

From the repository root, you can run the script with +./scripts/exps/assess-scan-on-repos.sh $URL_LIST_FILE where +$URL_LIST_FILE points to a line-delimited list of URLs you want to +run Kani on. Repositories that give warnings or errors can be grepping +for with "STDERR Warnings" and "Error exit in" respectively.

+

Performance comparisons with benchcomp

+

While Kani includes a performance regression suite, you may wish to test Kani's performance using your own benchmarks or with particular versions of Kani. +You can use the benchcomp tool in the Kani repository to run several 'variants' of a command on one or more benchmark suites; automatically parse the results of each of those suites; and take actions or emit visualizations based on those results.

+

Example use-cases

+
    +
  1. Run one or more benchmark suites with the current and previous versions of Kani. +Exit with a return code of 1 or print a custom summary to the terminal if any benchmark regressed by more than a user-configured amount.
  2. +
  3. Run benchmark suites using several historical versions of Kani and emit a graph of performance over time.
  4. +
  5. Run benchmark suites using different SAT solvers, command-line flags, or environment variables.
  6. +
+

Features

+

Benchcomp provides the following features to support your performance-comparison workflow:

+
    +
  • Automatically copies benchmark suites into a fresh directories before running with each variant, to ensure that built artifacts do not affect subsequent runtimes
  • +
  • Parses the results of different 'kinds' of benchmark suite and combines those results into a single unified format. +This allows you to run benchmarks from external repositories, suites of pre-compiled GOTO-binaries, and other kinds of benchmark all together and view their results in a single dashboard.
  • +
  • Driven by a single configuration file that can be sent to colleagues or checked into a repository to be used in continuous integration.
  • +
  • Extensible, allowing you to write your own parsers and visualizations.
  • +
  • Caches all previous runs and allows you to re-create visualizations for the latest run without actually re-running the suites.
  • +
+

Quick start

+

Here's how to run Kani's performance suite twice, comparing the last released version of Kani with the current HEAD.

+
cd $KANI_SRC_DIR
+git worktree add new HEAD
+git worktree add old $(git describe --tags --abbrev=0)
+
+tools/benchcomp/bin/benchcomp --config tools/benchcomp/configs/perf-regression.yaml
+
+

This uses the perf-regression.yaml configuration file that we use in continuous integration. +After running the suite twice, the configuration file terminates benchcomp with a return code of 1 if any of the benchmarks regressed on metrics such as success (a boolean), solver_runtime, and number_vccs (numerical). +Additionally, the config file directs benchcomp to print out a Markdown table that GitHub's CI summary page renders in to a table.

+

The rest of this documentation describes how to modify benchcomp for your own use cases, including writing a configuration file; writing a custom parser for your benchmark suite; and writing a custom visualization to examine the results of a performance comparison.

+

benchcomp command line

+

benchcomp is a single command that runs benchmarks, parses their results, combines these results, and emits visualizations. +benchcomp also provides subcommands to run these steps individually. +Most users will want to invoke benchcomp in one of two ways:

+
    +
  • benchcomp without any subcommands, which runs the entire performance comparison process as depicted below
  • +
  • benchcomp visualize, which runs the visualization step on the results of a previous benchmark run without actually re-running the benchmarks. +This is useful when tweaking the parameters of a visualization, for example changing the threshold of what is considered to be a regression.
  • +
+

The subcommands run and collate are also available. +The diagram below depicts benchcomp's order of operation.

Gcluster_runbenchcomp runcluster_collatebenchcomp collatecluster_vizualizebenchcomp visualizesuite_1asuite_1out_1aoutputfilessuite_1a->out_1arun withvariant asuite_1a_yamlsuite_1a.yamlout_1a->suite_1a_yamlsuite_1_parser.pyresult_yamlresult.yamlsuite_1a_yaml->result_yamlsuite_1bsuite_1out_1boutputfilessuite_1b->out_1brun withvariant bsuite_1b_yamlsuite_1b.yamlout_1b->suite_1b_yamlsuite_1_parser.pysuite_1b_yaml->result_yamlsuite_2csuite_2out_2coutputfilessuite_2c->out_2crun withvariant asuite_2c_yamlsuite_2c.yamlout_2c->suite_2c_yamlsuite_2_parser.pysuite_2c_yaml->result_yamlsuite_2dsuite_2out_2doutputfilessuite_2d->out_2drun withvariant bsuite_2d_yamlsuite_2d.yamlout_2d->suite_2d_yamlsuite_2_parser.pysuite_2d_yaml->result_yamlviz_1graph.svgresult_yaml->viz_1viz_2summary.mdresult_yaml->viz_2viz_3exit 1 onregressionresult_yaml->viz_3

+

Running benchcomp invokes run, collate, and visualize behind the scenes. +If you have previously run benchcomp, then running benchcomp visualize will emit the visualizations in the config file using the previous result.yaml.

+

In the diagram above, two different suites (1 and 2) are both run using two variants---combinations of command, working directory, and environment variables. +Benchmark suite 2 requires a totally different command line to suite 1---for example, suite_1 might contain Kani harnesses invoked through cargo kani, while suite_2 might contain CBMC harnesses invoked through run_cbmc_proofs.py. +Users would therefore define different variants (c and d) for invoking suite_2, and also specify a different parser to parse the results. +No matter how different the benchmark suites are, the collate stage combines their results so that they can later be compared.

+

Example config file

+

Users must specify the actual suites to run, the parsers used to collect their results, and the visualizations to emit in a file called benchcomp.yaml or a file passed to the -c/--config flag. +The next section describes the schema for this configuration file. +A run similar to the diagram above might be achieved using the following configuration file:

+
# Compare a range of Kani and CBMC benchmarks when
+# using Cadical versus the default SAT solver
+
+variants:
+  variant_a:
+    config:
+      directory: kani_benchmarks
+      command_line: scripts/kani-perf.sh
+      env: {}
+
+  variant_b:
+    config:
+      directory: kani_benchmarks
+      command_line: scripts/kani-perf.sh
+      # This variant uses a hypothetical environment variable that
+      # forces Kani to use the cadical SAT solver
+      env:
+        KANI_SOLVER: cadical
+
+  variant_c:
+    config:
+      directory: cbmc_benchmarks
+      command_line: run_cbmc_proofs.py
+      env: {}
+
+  variant_d:
+    config:
+      directory: cbmc_benchmarks
+      command_line: run_cbmc_proofs.py
+      env:
+        EXTERNAL_SAT_SOLVER: cadical
+
+run:
+  suites:
+    suite_1:
+      parser:
+        module: kani_perf
+      variants: [variant_a, variant_b]
+
+    suite_2:
+      parser:
+        module: cbmc_litani_parser
+      variants: [variant_c, variant_d]
+
+visualize:
+  - type: dump_graph
+    out_file: graph.svg
+
+  - type: dump_markdown_results_table
+    out_file: summary.md
+    extra_columns: []
+
+  - type: error_on_regression
+    variant_pairs:
+    - [variant_a, variant_b]
+    - [variant_c, variant_d]
+
+

benchcomp configuration file

+

benchcomp's operation is controlled through a YAML file---benchcomp.yaml by default or a file passed to the -c/--config option. +This page lists the different visualizations that are available.

+

Variants

+

A variant is a single invocation of a benchmark suite. Benchcomp runs several +variants, so that their performance can be compared later. A variant consists of +a command-line argument, working directory, and environment. Benchcomp invokes +the command using the operating system environment, updated with the keys and +values in env. If any values in env contain strings of the form ${var}, +Benchcomp expands them to the value of the environment variable $var.

+
variants:
+    variant_1:
+        config:
+            command_line: echo "Hello, world"
+            directory: /tmp
+            env:
+              PATH: /my/local/directory:${PATH}
+
+

Filters

+

After benchcomp has finished parsing the results, it writes the results to results.yaml by default. +Before visualizing the results (see below), benchcomp can filter the results by piping them into an external program.

+

To filter results before visualizing them, add filters to the configuration file.

+
filters:
+    - command_line: ./scripts/remove-redundant-results.py
+    - command_line: cat
+
+

The value of filters is a list of dicts. +Currently the only legal key for each of the dicts is command_line. +Benchcomp invokes each command_line in order, passing the results as a JSON file on stdin, and interprets the stdout as a YAML-formatted modified set of results. +Filter scripts can emit either YAML (which might be more readable while developing the script), or JSON (which benchcomp will parse as a subset of YAML).

+

Built-in visualizations

+

The following visualizations are available; these can be added to the visualize list of benchcomp.yaml.

+ +

Detailed documentation for these visualizations follows.

+

Plot

+

Scatterplot configuration options

+

dump_markdown_results_table

+

Print Markdown-formatted tables displaying benchmark results

+

For each metric, this visualization prints out a table of benchmarks, +showing the value of the metric for each variant, combined with an optional +scatterplot.

+

The 'out_file' key is mandatory; specify '-' to print to stdout.

+

'extra_colums' can be an empty dict. The sample configuration below assumes +that each benchmark result has a 'success' and 'runtime' metric for both +variants, 'variant_1' and 'variant_2'. It adds a 'ratio' column to the table +for the 'runtime' metric, and a 'change' column to the table for the +'success' metric. The 'text' lambda is called once for each benchmark. The +'text' lambda accepts a single argument---a dict---that maps variant +names to the value of that variant for a particular metric. The lambda +returns a string that is rendered in the benchmark's row in the new column. +This allows you to emit arbitrary text or markdown formatting in response to +particular combinations of values for different variants, such as +regressions or performance improvements.

+

'scatterplot' takes the values 'off' (default), 'linear' (linearly scaled +axes), or 'log' (logarithmically scaled axes).

+

Sample configuration:

+
visualize:
+- type: dump_markdown_results_table
+  out_file: "-"
+  scatterplot: linear
+  extra_columns:
+    runtime:
+    - column_name: ratio
+      text: >
+        lambda b: str(b["variant_2"]/b["variant_1"])
+        if b["variant_2"] < (1.5 * b["variant_1"])
+        else "**" + str(b["variant_2"]/b["variant_1"]) + "**"
+    success:
+    - column_name: change
+      text: >
+        lambda b: "" if b["variant_2"] == b["variant_1"]
+        else "newly passing" if b["variant_2"]
+        else "regressed"
+
+

Example output:

+
## runtime
+
+| Benchmark |  variant_1 | variant_2 | ratio |
+| --- | --- | --- | --- |
+| bench_1 | 5 | 10 | **2.0** |
+| bench_2 | 10 | 5 | 0.5 |
+
+## success
+
+| Benchmark |  variant_1 | variant_2 | change |
+| --- | --- | --- | --- |
+| bench_1 | True | True |  |
+| bench_2 | True | False | regressed |
+| bench_3 | False | True | newly passing |
+
+

dump_yaml

+

Print the YAML-formatted results to a file.

+

The 'out_file' key is mandatory; specify '-' to print to stdout.

+

Sample configuration:

+
visualize:
+- type: dump_yaml
+  out_file: '-'
+
+

error_on_regression

+

Terminate benchcomp with a return code of 1 if any benchmark regressed.

+

This visualization checks whether any benchmark regressed from one variant +to another. Sample configuration:

+
visualize:
+- type: error_on_regression
+  variant_pairs:
+  - [variant_1, variant_2]
+  - [variant_1, variant_3]
+  checks:
+  - metric: runtime
+    test: "lambda old, new: new / old > 1.1"
+  - metric: passed
+    test: "lambda old, new: False if not old else not new"
+
+

This says to check whether any benchmark regressed when run under variant_2 +compared to variant_1. A benchmark is considered to have regressed if the +value of the 'runtime' metric under variant_2 is 10% higher than the value +under variant_1. Furthermore, the benchmark is also considered to have +regressed if it was previously passing, but is now failing. These same +checks are performed on all benchmarks run under variant_3 compared to +variant_1. If any of those lambda functions returns True, then benchcomp +will terminate with a return code of 1.

+

run_command

+

Run an executable command, passing the performance metrics as JSON on stdin.

+

This allows you to write your own visualization, which reads a result file +on stdin and does something with it, e.g. writing out a graph or other +output file.

+

Sample configuration:

+
visualize:
+- type: run_command
+  command: ./my_visualization.py
+
+

Custom parsers

+

Benchcomp ships with built-in parsers that retrieve the results of a benchmark suite after the run has completed. +You can also create your own parser, either to run locally or to check into the Kani codebase.

+

Built-in parsers

+

You specify which parser should run for each benchmark suite in benchcomp.yaml. +For example, if you're running the kani performance suite, you would use the built-in kani_perf parser to parse the results:

+
suites:
+    my_benchmark_suite:
+      variants: [variant_1, variant_2]
+      parser:
+        module: kani_perf
+
+

Custom parsers

+

A parser is a program that benchcomp runs inside the root directory of a benchmark suite, after the suite run has completed. +The parser should retrieve the results of the run (by parsing output files etc.) and print the results out as a YAML document. +You can use your executable parser by specifying the command key rather than the module key in your benchconf.yaml file:

+
suites:
+    my_benchmark_suite:
+      variants: [variant_1, variant_2]
+      parser:
+        command: ./my-cool-parser.sh
+
+

The kani_perf parser mentioned above, in tools/benchcomp/benchcomp/parsers/kani_perf.py, is a good starting point for writing a custom parser, as it also works as a standalone executable. +Here is an example output from an executable parser:

+
metrics:
+    runtime: {}
+    success: {}
+    errors: {}
+benchmarks:
+    bench_1:
+        metrics:
+            runtime: 32
+            success: true
+            errors: []
+    bench_2:
+        metrics:
+            runtime: 0
+            success: false
+            errors: ["compilation failed"]
+
+

The above format is different from the final result.yaml file that benchcomp writes, because the above file represents the output of running a single benchmark suite using a single variant. +Your parser will run once for each variant, and benchcomp combines the dictionaries into the final result.yaml file.

+

Contributing custom parsers to Kani

+

To turn your executable parser into one that benchcomp can invoke as a module, ensure that it has a main(working_directory) method that returns a dict (the same dict that it would print out as a YAML file to stdout). +Save the file in tools/benchcomp/benchcomp/parsers using python module naming conventions (filename should be an identifier and end in .py).

+

Limitations

+

Like other tools, Kani comes with some limitations. In some cases, these +limitations are inherent because of the techniques it's based on, or the +undecidability of the properties that Kani seeks to prove. In other +cases, it's just a matter of time and effort to remove these limitations (e.g., +specific unsupported Rust language features).

+

In this chapter, we do the following to document these limitations:

+ +

Undefined Behaviour

+

The Effect of Undefined Behaviour on Program Verification

+

Rust has a broad definition of undefined behaviour (UB). +The Rust documentation warns that UB can have unexpected, non-local effects:

+
+

Note: Undefined behavior affects the entire program. For example, calling a function in C that exhibits undefined behavior of C means your entire program contains undefined behaviour that can also affect the Rust code. And vice versa, undefined behavior in Rust can cause adverse affects on code executed by any FFI calls to other languages.

+
+

If a program has UB, the semantics of the rest of the program are undefined. +As a result, if the program under verification contains UB then, in principle, the program (including its representation in MIR analyzed by Kani) has no semantics and hence could do anything, including violating the guarantees checked by Kani. +This means that verification results are subject to the proviso that the program under verification does not contain UB.

+

What forms of Undefined Behaviour can Rust Exhibit

+

Rust’s definition of UB is so broad that Rust has the following warning:

+
+

Warning +The following list is not exhaustive. There is no formal model of Rust's semantics for what is and is not allowed in unsafe code, so there may be more behavior considered unsafe. The following list is just what we know for sure is undefined behavior. Please read the Rustonomicon (https://doc.rust-lang.org/nomicon/index.html) before writing unsafe code.

+
+

Given the lack of a formal semantics for UB, and given Kani's focus on memory safety, there are classes of UB which Kani does not detect. +A non-exhaustive list of these, based on the non-exhaustive list from the Rust documentation, is:

+
    +
  • Data races. +
      +
    • Kani focuses on sequential code.
    • +
    +
  • +
  • Breaking the pointer aliasing rules (http://llvm.org/docs/LangRef.html#pointer-aliasing-rules). +
      +
    • Kani can detect if misuse of pointers causes memory safety or assertion violations, but does not track reference lifetimes.
    • +
    +
  • +
  • Mutating immutable data. +
      +
    • Kani can detect if modification of immutable data causes memory safety or assertion violations, but does not track reference lifetimes.
    • +
    +
  • +
  • Invoking undefined behavior via compiler intrinsics. +
      +
    • Kani makes a best effort attempt to check the preconditions of compiler intrinsics, but does not guarantee to do so in all cases.
    • +
    +
  • +
  • Executing code compiled with platform features that the current platform does not support (see target_feature). +
      +
    • Kani relies on rustc to check for this case.
    • +
    +
  • +
  • Calling a function with the wrong call ABI or unwinding from a function with the wrong unwind ABI. +
      +
    • Kani relies on rustc to check for this case.
    • +
    +
  • +
  • Producing an invalid value, even in private fields and locals. +
      +
    • Kani won't create invalid values with kani::any() but it also won't complain if you transmute an invalid value to a Rust type (for example, a 0 to NonZeroU32).
    • +
    +
  • +
  • Incorrect use of inline assembly. +
      +
    • Kani does not support inline assembly.
    • +
    +
  • +
  • Using uninitialized memory. + +
  • +
+

Kani makes a best-effort attempt to detect some cases of UB:

+ +

Rust feature support

+

The table below tries to summarize the current support in Kani for +the Rust language features according to the Rust Reference. +We use the following values to indicate the level of support:

+
    +
  • Yes: The feature is fully supported. We are not aware of any issue with it.
  • +
  • Partial: The feature is at least partially supported. We are aware of some issue with +with it.
  • +
  • No: The feature is not supported. Some support may be available but analyses should not be trusted.
  • +
+

As with all software, bugs may be found anywhere regardless of the level of support. In such cases, we +would greatly appreciate that you filed a bug report.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ReferenceFeatureSupportNotes
3.1Macros By ExampleYes
3.2Procedural MacrosYes
4Crates and source filesYes
5Conditional compilationYes
6.1ModulesYes
6.2Extern cratesYes
6.3Use declarationsYes
6.4FunctionsYes
6.5Type aliasesYes
6.6StructsYes
6.7EnumerationsYes
6.8UnionsYes
6.9Constant itemsYes
6.10Static itemsYes
6.11TraitsYes
6.12ImplementationsYes
6.13External blocksYes
6.14Generic parametersYes
6.15Associated ItemsYes
7AttributesYes
8.1StatementsYes
8.2.1Literal expressionsYes
8.2.2Path expressionsYes
8.2.3Block expressionsYes
8.2.4Operator expressionsYes
8.2.5Grouped expressionsYes
8.2.6Array and index expressionsYes
8.2.7Tuple and index expressionsYes
8.2.8Struct expressionsYes
8.2.9Call expressionsYes
8.2.10Method call expressionsYes
8.2.11Field access expressionsYes
8.2.12Closure expressionsYes
8.2.13Loop expressionsYes
8.2.14Range expressionsYes
8.2.15If and if let expressionsYes
8.2.16Match expressionsYes
8.2.17Return expressionsYes
8.2.18Await expressionsNoSee Notes - Concurrency
9PatternsPartial#707
10.1.1Boolean typeYes
10.1.2Numeric typesYes
10.1.3Textual typesYes
10.1.4Never typeYes
10.1.5Tuple typesYes
10.1.6Array typesYes
10.1.7Slice typesYes
10.1.8Struct typesYes
10.1.9Enumerated typesYes
10.1.10Union typesYes
10.1.11Function item typesYes
10.1.12Closure typesPartialSee Notes - Advanced features
10.1.13Pointer typesPartialSee Notes - Advanced features
10.1.14Function pointer typesPartialSee Notes - Advanced features
10.1.15Trait object typesPartialSee Notes - Advanced features
10.1.16Impl trait typePartialSee Notes - Advanced features
10.1.17Type parametersPartialSee Notes - Advanced features
10.1.18Inferred typePartialSee Notes - Advanced features
10.2Dynamically Sized TypesPartialSee Notes - Advanced features
10.3Type layoutYes
10.4Interior mutabilityYes
10.5Subtyping and VarianceYes
10.6Trait and lifetime boundsYes
10.7Type coercionsPartialSee Notes - Advanced features
10.8DestructorsPartial
10.9Lifetime elisionYes
11Special types and traitsPartial
Box<T>Yes
Rc<T>Yes
Arc<T>Yes
Pin<T>Yes
UnsafeCell<T>Partial
PhantomData<T>Partial
Operator TraitsPartial
Deref and DerefMutYes
DropPartial
CopyYes
CloneYes
14LinkageYes
15.1Unsafe functionsYes
15.2Unsafe blocksYes
15.3Behavior considered undefinedPartial
Data racesNoSee Notes - Concurrency
Dereferencing dangling raw pointersYes
Dereferencing unaligned raw pointersNo
Breaking pointer aliasing rulesNo
Mutating immutable dataNo
Invoking undefined behavior via compiler intrinsicsPartialSee Notes - Intrinsics
Executing code compiled with platform features that the current platform does not supportNo
Producing an invalid value, even in private fields and localsNo
+

Notes on partially or unsupported features

+

Code generation for unsupported features

+

Kani aims to be an industrial verification tool. Most industrial crates may +include unsupported features in parts of their code that do not need to be +verified. In general, this should not prevent users using Kani to verify their code.

+

Because of that, the general rule is that Kani generates an assert(false) +statement followed by an assume(false) statement when compiling any +unsupported feature. assert(false) will cause verification to fail if the +statement is reachable during the verification stage, while assume(false) will +block any further exploration of the path. However, the analysis will not be +affected if the statement is not reachable from the code under verification, so +users can still verify components of their code that do not use unsupported +features.

+

In a few cases, Kani aborts execution if the analysis could be affected in +some way because of an unsupported feature (e.g., global ASM).

+

Assembly

+

Kani does not support assembly code for now. We may add it in the future but at +present there are no plans to do so.

+

Check out the tracking issues for inline assembly (asm! +macro) and global assembly +(asm_global! macro) to know +more about the current status.

+

Concurrency

+

Concurrent features are currently out of scope for Kani. In general, the +verification of concurrent programs continues to be an open research problem +where most tools that analyze concurrent code lack support for other features. +Because of this, Kani emits a warning whenever it encounters concurrent code and +compiles as if it was sequential code.

+

Standard library functions

+

Kani overrides a few common functions +(e.g., print macros) to provide a more verification friendly implementation.

+

Advanced features

+

The semantics around some advanced features (traits, types, etc.) from Rust are +not formally defined which makes it harder to ensure that we can properly model +all their use cases.

+

In particular, there are some outstanding issues to note here:

+
    +
  • Sanity check Variant type in projections +#448.
  • +
  • Unexpected fat pointer results in +#277, +#327 and +#676.
  • +
+

We are particularly interested in bug reports concerning +these features, so please file a bug +report +if you're aware of one.

+

Panic strategies

+

Rust has two different strategies when a panic occurs:

+
    +
  1. Stack unwinding (default): Walks back the stack cleaning up the data from +each function it encounters.
  2. +
  3. Abortion: Immediately ends the program without cleaning up.
  4. +
+

Currently, Kani does not support stack unwinding. This has some implications +regarding memory safety since programs sometimes rely on the unwinding logic to +ensure there is no resource leak or persistent data inconsistency. Check out +this issue for updates on +stack unwinding support.

+

Uninitialized memory

+

Reading uninitialized memory is +considered undefined behavior in Rust. +At the moment, Kani cannot detect if memory is uninitialized, but in practice +this is mitigated by the fact that all memory is initialized with +nondeterministic values. +Therefore, any code that depends on uninitialized data will exhibit nondeterministic behavior. +See this issue for more details.

+

Destructors

+

At present, we are aware of some issues with destructors, in particular those +related to advanced features.

+

Intrinsics

+

Please refer to Intrinsics for information +on the current support in Kani for Rust compiler intrinsics.

+

Floating point operations

+

Kani supports floating point numbers, but some supported operations on floats are "over-approximated." +These are the trigonometric functions like sin and cos and the sqrt function as well. +This means the verifier can raise errors that cannot actually happen when the code is run normally. +For instance, (#1342) the sin/cos functions basically return a nondeterministic value between -1 and 1. +In other words, they largely ignore their input and give very conservative answers. +This range certainly includes the "real" value, so proof soundness is still preserved, but it means Kani could raise spurious errors that cannot actually happen. +This makes Kani unsuitable for verifying some kinds of properties (e.g. precision) about numerical algorithms. +Proofs that fail because of this problem can sometimes be repaired by introducing "stubs" for these functions that return a more acceptable approximation. +However, note that the actual behavior of these functions can vary by platform/os/architecture/compiler, so introducing an "overly precise" approximation may introduce unsoundness: actual system behavior may produce different values from the stub's approximation.

+

Intrinsics

+

The tables below try to summarize the current support in Kani for Rust intrinsics. +We define the level of support similar to how we indicate Rust feature support:

+
    +
  • Yes: The intrinsic is fully supported. We are not aware of any issue with it.
  • +
  • Partial: The intrinsic is at least partially supported. We are aware of some issue with +with it.
  • +
  • No: The intrinsic is not supported.
  • +
+

In general, code generation for unsupported intrinsics follows the rule +described in Rust feature support - Code generation for unsupported +features.

+

Any intrinsic not appearing in the tables below is considered not supported. +Please open a feature request +if your code depends on an unsupported intrinsic.

+

Compiler intrinsics

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSupportNotes
abortYes
add_with_overflowYes
arith_offsetYes
assert_inhabitedYes
assert_uninit_validYes
assert_zero_validYes
assumeYes
atomic_and_seqcstPartialSee Atomics
atomic_and_acquirePartialSee Atomics
atomic_and_acqrelPartialSee Atomics
atomic_and_releasePartialSee Atomics
atomic_and_relaxedPartialSee Atomics
atomic_cxchg_acqrel_acquirePartialSee Atomics
atomic_cxchg_acqrel_relaxedPartialSee Atomics
atomic_cxchg_acqrel_seqcstPartialSee Atomics
atomic_cxchg_acquire_acquirePartialSee Atomics
atomic_cxchg_acquire_relaxedPartialSee Atomics
atomic_cxchg_acquire_seqcstPartialSee Atomics
atomic_cxchg_relaxed_acquirePartialSee Atomics
atomic_cxchg_relaxed_relaxedPartialSee Atomics
atomic_cxchg_relaxed_seqcstPartialSee Atomics
atomic_cxchg_release_acquirePartialSee Atomics
atomic_cxchg_release_relaxedPartialSee Atomics
atomic_cxchg_release_seqcstPartialSee Atomics
atomic_cxchg_seqcst_acquirePartialSee Atomics
atomic_cxchg_seqcst_relaxedPartialSee Atomics
atomic_cxchg_seqcst_seqcstPartialSee Atomics
atomic_cxchgweak_acqrel_acquirePartialSee Atomics
atomic_cxchgweak_acqrel_relaxedPartialSee Atomics
atomic_cxchgweak_acqrel_seqcstPartialSee Atomics
atomic_cxchgweak_acquire_acquirePartialSee Atomics
atomic_cxchgweak_acquire_relaxedPartialSee Atomics
atomic_cxchgweak_acquire_seqcstPartialSee Atomics
atomic_cxchgweak_relaxed_acquirePartialSee Atomics
atomic_cxchgweak_relaxed_relaxedPartialSee Atomics
atomic_cxchgweak_relaxed_seqcstPartialSee Atomics
atomic_cxchgweak_release_acquirePartialSee Atomics
atomic_cxchgweak_release_relaxedPartialSee Atomics
atomic_cxchgweak_release_seqcstPartialSee Atomics
atomic_cxchgweak_seqcst_acquirePartialSee Atomics
atomic_cxchgweak_seqcst_relaxedPartialSee Atomics
atomic_cxchgweak_seqcst_seqcstPartialSee Atomics
atomic_fence_seqcstPartialSee Atomics
atomic_fence_acquirePartialSee Atomics
atomic_fence_acqrelPartialSee Atomics
atomic_fence_releasePartialSee Atomics
atomic_load_seqcstPartialSee Atomics
atomic_load_acquirePartialSee Atomics
atomic_load_relaxedPartialSee Atomics
atomic_load_unorderedPartialSee Atomics
atomic_max_seqcstPartialSee Atomics
atomic_max_acquirePartialSee Atomics
atomic_max_acqrelPartialSee Atomics
atomic_max_releasePartialSee Atomics
atomic_max_relaxedPartialSee Atomics
atomic_min_seqcstPartialSee Atomics
atomic_min_acquirePartialSee Atomics
atomic_min_acqrelPartialSee Atomics
atomic_min_releasePartialSee Atomics
atomic_min_relaxedPartialSee Atomics
atomic_nand_seqcstPartialSee Atomics
atomic_nand_acquirePartialSee Atomics
atomic_nand_acqrelPartialSee Atomics
atomic_nand_releasePartialSee Atomics
atomic_nand_relaxedPartialSee Atomics
atomic_or_seqcstPartialSee Atomics
atomic_or_acquirePartialSee Atomics
atomic_or_acqrelPartialSee Atomics
atomic_or_releasePartialSee Atomics
atomic_or_relaxedPartialSee Atomics
atomic_singlethreadfence_seqcstPartialSee Atomics
atomic_singlethreadfence_acquirePartialSee Atomics
atomic_singlethreadfence_acqrelPartialSee Atomics
atomic_singlethreadfence_releasePartialSee Atomics
atomic_store_seqcstPartialSee Atomics
atomic_store_releasePartialSee Atomics
atomic_store_relaxedPartialSee Atomics
atomic_store_unorderedPartialSee Atomics
atomic_umax_seqcstPartialSee Atomics
atomic_umax_acquirePartialSee Atomics
atomic_umax_acqrelPartialSee Atomics
atomic_umax_releasePartialSee Atomics
atomic_umax_relaxedPartialSee Atomics
atomic_umin_seqcstPartialSee Atomics
atomic_umin_acquirePartialSee Atomics
atomic_umin_acqrelPartialSee Atomics
atomic_umin_releasePartialSee Atomics
atomic_umin_relaxedPartialSee Atomics
atomic_xadd_seqcstPartialSee Atomics
atomic_xadd_acquirePartialSee Atomics
atomic_xadd_acqrelPartialSee Atomics
atomic_xadd_releasePartialSee Atomics
atomic_xadd_relaxedPartialSee Atomics
atomic_xchg_seqcstPartialSee Atomics
atomic_xchg_acquirePartialSee Atomics
atomic_xchg_acqrelPartialSee Atomics
atomic_xchg_releasePartialSee Atomics
atomic_xchg_relaxedPartialSee Atomics
atomic_xor_seqcstPartialSee Atomics
atomic_xor_acquirePartialSee Atomics
atomic_xor_acqrelPartialSee Atomics
atomic_xor_releasePartialSee Atomics
atomic_xor_relaxedPartialSee Atomics
atomic_xsub_seqcstPartialSee Atomics
atomic_xsub_acquirePartialSee Atomics
atomic_xsub_acqrelPartialSee Atomics
atomic_xsub_releasePartialSee Atomics
atomic_xsub_relaxedPartialSee Atomics
blackboxYes
bitreverseYes
breakpointYes
bswapYes
caller_locationNo
ceilf32Yes
ceilf64Yes
copyYes
copy_nonoverlappingYes
copysignf32Yes
copysignf64Yes
cosf32PartialResults are overapproximated; this test explains how
cosf64PartialResults are overapproximated; this test explains how
ctlzYes
ctlz_nonzeroYes
ctpopYes
cttzYes
cttz_nonzeroYes
discriminant_valueYes
drop_in_placeNo
exact_divYes
exp2f32PartialResults are overapproximated
exp2f64PartialResults are overapproximated
expf32PartialResults are overapproximated
expf64PartialResults are overapproximated
fabsf32Yes
fabsf64Yes
fadd_fastYes
fdiv_fastPartial#809
float_to_int_uncheckedPartial#3629
floorf32Yes
floorf64Yes
fmaf32PartialResults are overapproximated
fmaf64PartialResults are overapproximated
fmul_fastPartial#809
forgetYes
frem_fastNo
fsub_fastYes
likelyYes
log10f32PartialResults are overapproximated
log10f64PartialResults are overapproximated
log2f32PartialResults are overapproximated
log2f64PartialResults are overapproximated
logf32PartialResults are overapproximated
logf64PartialResults are overapproximated
maxnumf32Yes
maxnumf64Yes
min_align_ofYes
min_align_of_valYes
minnumf32Yes
minnumf64Yes
move_val_initNo
mul_with_overflowYes
nearbyintf32Yes
nearbyintf64Yes
needs_dropYes
nontemporal_storeNo
offsetPartialDoesn't check all UB conditions
powf32PartialResults are overapproximated
powf64PartialResults are overapproximated
powif32PartialResults are overapproximated
powif64PartialResults are overapproximated
pref_align_ofYes
prefetch_read_dataNo
prefetch_read_instructionNo
prefetch_write_dataNo
prefetch_write_instructionNo
ptr_guaranteed_eqYes
ptr_guaranteed_neYes
ptr_offset_fromPartialDoesn't check all UB conditions
raw_eqPartialCannot detect uninitialized memory
rintf32Yes
rintf64Yes
rotate_leftYes
rotate_rightYes
roundf32Yes
roundf64Yes
rustc_peekNo
saturating_addYes
saturating_subYes
sinf32PartialResults are overapproximated; this test explains how
sinf64PartialResults are overapproximated; this test explains how
size_ofYes
size_of_valYes
sqrtf32PartialResults are overapproximated
sqrtf64PartialResults are overapproximated
sub_with_overflowYes
transmutePartialDoesn't check all UB conditions
truncf32Yes
truncf64Yes
tryNo#267
type_idYes
type_nameYes
typed_swap_nonoverlappingYes
unaligned_volatile_loadNoSee Notes - Concurrency
unaligned_volatile_storeNoSee Notes - Concurrency
unchecked_addYes
unchecked_divYes
unchecked_mulYes
unchecked_remYes
unchecked_shlYes
unchecked_shrYes
unchecked_subYes
unlikelyYes
unreachableYes
variant_countNo
volatile_copy_memoryNoSee Notes - Concurrency
volatile_copy_nonoverlapping_memoryNoSee Notes - Concurrency
volatile_loadPartialSee Notes - Concurrency
volatile_set_memoryNoSee Notes - Concurrency
volatile_storePartialSee Notes - Concurrency
wrapping_addYes
wrapping_mulYes
wrapping_subYes
write_bytesYes
+

Atomics

+

All atomic intrinsics are compiled as an atomic block where the operation is +performed. But as noted in Notes - Concurrency, Kani support for +concurrent verification is limited and not used by default. Verification on code +containing atomic intrinsics should not be trusted given that Kani assumes the +code to be sequential.

+

Platform intrinsics

+

Intrinsics from the platform_intrinsics feature.

+ + + + + + + + + + + + + + + + + + + + +
NameSupportNotes
simd_addYes
simd_andYes
simd_divYes
simd_eqYes
simd_extractYes
simd_geYes
simd_gtYes
simd_insertYes
simd_leYes
simd_ltYes
simd_mulYes
simd_neYes
simd_orYes
simd_remYesDoesn't check for floating point overflow #2669
simd_shlYes
simd_shrYes
simd_shuffle*Yes
simd_subYes
simd_xorYes
+

Unstable features

+

In general, unstable Rust features are out of scope and any support +for them available in Kani should be considered unstable as well.

+

The following are examples of unstable features that are not supported +in Kani:

+
    +
  • Generators
  • +
  • C-variadics
  • +
+

Overrides

+

As explained in Comparison with other +tools, Kani is based on a +technique called model checking, which verifies a program without actually +executing it. It does so through encoding the program and analyzing the encoded +version. The encoding process often requires "modeling" some of the library +functions to make them suitable for analysis. Typical examples of functionality +that requires modeling are system calls and I/O operations. In some cases, Kani +performs such encoding through overriding some of the definitions in the Rust +standard library.

+

The following table lists some of the symbols that Kani +overrides and a description of their behavior compared to the std versions:

+ + + + + + +
NameDescription
assert, assert_eq, and assert_ne macrosSkips string formatting code, generates a more informative message and performs some instrumentation
debug_assert, debug_assert_eq, and debug_assert_ne macrosRewrites as equivalent assert* macro
print, eprint, println, and eprintln macrosSkips string formatting and I/O operations
unreachable macroSkips string formatting and invokes panic!()
std::process::{abort, exit} functionsInvokes panic!() to abort the execution
+

FAQs

+

This section collects frequently asked questions about Kani. +Please consider opening an issue if you have a question that would like to see here.

+

Questions

+
+Kani doesn't fail after kani::assume(false). Why? +
+

kani::assume(false) (or kani::assume(cond) where cond is a condition that results in false in the context of the program), won't cause errors in Kani. +Instead, such an assumption has the effect of blocking all the symbolic execution paths from the assumption. +Therefore, all checks after the assumption should appear as UNREACHABLE. +That's the expected behavior for kani::assume(false) in Kani.

+

If you didn't expect certain checks in a harness to be UNREACHABLE, we recommend using the kani::cover macro to determine what conditions are possible in case you've over-constrained the harness.

+
+
+I implemented the kani::Arbitrary trait for a type that's not from my crate, and got the error +only traits defined in the current crate can be implemented for types defined outside of the crate. +What does this mean? What can I do? +
+

This error is due to a violation of Rust's orphan rules for trait implementations, which are explained here. +In that case, you'll need to write a function that builds an object from non-deterministic variables. +Inside this function you would simply return an arbitrary value by generating arbitrary values for its components.

+

For example, let's assume the type you're working with is this enum:

+
#[derive(Copy, Clone)]
+pub enum Rating {
+    One,
+    Two,
+    Three,
+}
+
+

Then, you can match on a non-deterministic integer (supplied by kani::any) to return non-deterministic Rating variants:

+
    pub fn any_rating() -> Rating {
+        match kani::any() {
+            0 => Rating::One,
+            1 => Rating::Two,
+            _ => Rating::Three,
+        }
+    }
+
+

More details about this option, which also useful in other cases, can be found here.

+

If the type comes from std (Rust's standard library), you can open a request for adding Arbitrary implementations to the Kani library. +Otherwise, there are more involved options to consider:

+
    +
  1. Importing a copy of the external crate that defines the type, then implement Arbitrary there.
  2. +
  3. Contributing the Arbitrary implementation to the external crate that defines the type.
  4. +
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + diff --git a/reference.html b/reference.html new file mode 100644 index 000000000000..e028bd02f093 --- /dev/null +++ b/reference.html @@ -0,0 +1,185 @@ + + + + + + Reference - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Reference

+

This section is the main reference for Kani. +It contains sections that informally describe its main features.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/reference/attributes.html b/reference/attributes.html new file mode 100644 index 000000000000..ca8ff3dd0ae3 --- /dev/null +++ b/reference/attributes.html @@ -0,0 +1,365 @@ + + + + + + Attributes - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Attributes

+

In Kani, attributes are used to mark functions as harnesses and control their execution. +This section explains the attributes available in Kani and how they affect the verification process.

+

At present, the available Kani attributes are the following:

+ +

#[kani::proof]

+

The #[kani::proof] attribute specifies that a function is a proof harness.

+

Proof harnesses are similar to test harnesses, especially property-based test harnesses, +and they may use functions from the Kani API (e.g., kani::any()). +A proof harness is the smallest verification unit in Kani.

+

When Kani is run, either through kani or cargo kani, it'll first collect all proof harnesses +(i.e., functions with the attribute #[kani::proof]) and then attempt to verify them.

+

Example

+

If we run Kani on this example:

+
#[kani::proof]
+fn my_harness() {
+    assert!(1 + 1 == 2);
+}
+
+

We should see a line in the output that says Checking harness my_harness... (assuming my_harness is the only harness in our code). +This will be followed by multiple messages that come from CBMC (the verification engine used by Kani) and the verification results.

+

Using any other Kani attribute without #[kani::proof] will result in compilation errors.

+

Limitations

+

The #[kani::proof] attribute can only be added to functions without parameters.

+

#[kani::should_panic]

+

The #[kani::should_panic] attribute specifies that a proof harness is expected to panic.

+

This attribute allows users to exercise negative verification. +It's analogous to how #[should_panic] allows users to exercise negative testing for Rust unit tests.

+

This attribute only affects the overall verification result. +In particular, using the #[kani::should_panic] attribute will return one of the following results:

+
    +
  • VERIFICATION:- FAILED (encountered no panics, but at least one was expected) if there were no failed checks.
  • +
  • VERIFICATION:- FAILED (encountered failures other than panics, which were unexpected) if there were failed checks but not all them were related to panics.
  • +
  • VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected) otherwise.
  • +
+

At the moment, to determine if a check is related to a panic, we check if its class is assertion. +The class is the second member in the property name, the triple that's printed after Check X: : <function>.<class>.<number>. +For example, the class in Check 1: my_harness.assertion.1 is assertion, so this check is considered to be related to a panic.

+
+

NOTE: The #[kani::should_panic] is only recommended for writing +harnesses which complement existing harnesses that don't use the same +attribute. In other words, it's only recommended to write negative harnesses +after having written positive harnesses that successfully verify interesting +properties about the function under verification.

+
+

Limitations

+

The #[kani::should_panic] attribute verifies that there are one or more failed checks related to panics. +At the moment, it's not possible to pin it down to specific panics. +Therefore, it's possible that the panics detected with #[kani::should_panic] aren't the ones that were originally expected after a change in the code under verification.

+

Example

+

Let's assume we're using the Device from this example:

+
struct Device {
+    is_init: bool,
+}
+
+impl Device {
+    fn new() -> Self {
+        Device { is_init: false }
+    }
+
+    fn init(&mut self) {
+        assert!(!self.is_init);
+        self.is_init = true;
+    }
+}
+
+

We may want to verify that calling device.init() more than once should result in a panic. +We can do so with the following harness:

+
#[kani::proof]
+#[kani::should_panic]
+fn cannot_init_device_twice() {
+    let mut device = Device::new();
+    device.init();
+    device.init();
+}
+
+

Running Kani on it will produce the result VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected)

+

#[kani::unwind(<number>)]

+

The #[kani::unwind(<number>)] attribute specifies that all loops must be unwound up to <number> times.

+

By default, Kani attempts to unwind all loops automatically. +However, this unwinding process doesn't always terminate. +The #[kani::unwind(<number>)] attribute will:

+
    +
  1. Disable automatic unwinding.
  2. +
  3. Unwind all loops up to <number> times.
  4. +
+

After the unwinding stage, Kani will attempt to verify the harness. +If the #[kani::unwind(<number>)] attribute was specified, there's a chance that one or more loops weren't unwound enough times. +In that case, there will be at least one failed unwinding assertion (there's one unwinding assertion for each loop), causing verification to fail.

+

Check the Loops, unwinding and bounds section for more information about unwinding.

+

Example

+

Let's assume we've written this code which contains a loop:

+
fn my_sum(vec: &Vec<u32>) -> u32 {
+    let mut sum = 0;
+    for elem in vec {
+        sum += elem;
+    }
+    sum
+}
+
+#[kani::proof]
+fn my_harness() {
+    let vec = vec![1, 2, 3];
+    let sum = my_sum(&vec);
+    assert!(sum == 6);
+}
+
+

Running this example on Kani will produce a successful verification result. +In this case, Kani automatically finds the required unwinding value (i.e., the number of times it needs to unwind all loops). +This means that the #[kani::unwind(<number>)] attribute isn't needed, as we'll see soon. +In general, the required unwinding value is equal to the maximum number of iterations for all loops, plus one. +The required unwinding value in this example is 4: the 3 iterations in the for elem in vec loop, plus 1.

+

Let's see what happens if we force a lower unwinding value with #[kani::unwind(3)]:

+
#[kani::proof]
+#[kani::unwind(3)]
+fn my_harness() {
+    let vec = vec![1, 2, 3];
+    let sum = my_sum(&vec);
+    assert!(sum == 6);
+}
+
+

As we mentioned, trying to verify this harness causes an unwinding failure:

+
SUMMARY:
+ ** 1 of 187 failed (186 undetermined)
+Failed Checks: unwinding assertion loop 0
+ File: "/home/ubuntu/devices/src/main.rs", line 32, in my_sum
+
+VERIFICATION:- FAILED
+[Kani] info: Verification output shows one or more unwinding failures.
+[Kani] tip: Consider increasing the unwinding value or disabling `--unwinding-assertions`.
+
+

Kani cannot verify the harness because there is at least one unwinding assertion failure. +But, if we use #[kani::unwind(4)], which is the right unwinding value we computed earlier:

+
#[kani::proof]
+#[kani::unwind(4)]
+fn my_harness() {
+    let vec = vec![1, 2, 3];
+    let sum = my_sum(&vec);
+    assert!(sum == 6);
+}
+
+

We'll get a successful result again:

+
SUMMARY:
+ ** 0 of 186 failed
+
+VERIFICATION:- SUCCESSFUL
+
+

#[kani::solver(<solver>)]

+

Changes the solver to be used by Kani's verification engine (CBMC).

+

This may change the verification time required to verify a harness.

+

At present, <solver> can be one of:

+
    +
  • minisat: MiniSat.
  • +
  • cadical (default): CaDiCaL.
  • +
  • kissat: kissat.
  • +
  • bin="<SAT_SOLVER_BINARY>": A custom solver binary, "<SAT_SOLVER_BINARY>", that must be in path.
  • +
+

Example

+

Kani will use the CaDiCaL solver in the following example:

+
#[kani::proof]
+#[kani::solver(cadical)]
+fn check() {
+    let mut a = [2, 3, 1];
+    a.sort();
+    assert_eq!(a[0], 1);
+    assert_eq!(a[1], 2);
+    assert_eq!(a[2], 3);
+}
+
+

Changing the solver may result in different verification times depending on the harness.

+

Note that the default solver may vary depending on Kani's version. +We highly recommend users to annotate their harnesses if the choice of solver +has a major impact on performance, even if the solver used is the current +default one.

+

#[kani::stub(<original>, <replacement>)]

+

Replaces the function/method with name with the function/method with name during compilation

+

Check the Stubbing section for more information about stubbing.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/reference/experimental/autoharness.html b/reference/experimental/autoharness.html new file mode 100644 index 000000000000..6509ac76d8be --- /dev/null +++ b/reference/experimental/autoharness.html @@ -0,0 +1,266 @@ + + + + + + Automatic Harness Generation - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Automatic Harness Generation

+

Recall the harness for estimate_size that we wrote in First Steps:

+
#[cfg(kani)]
+#[kani::proof]
+fn check_estimate_size() {
+    let x: u32 = kani::any();
+    estimate_size(x);
+}
+
+

This harness first declares a local variable x using kani::any(), then calls estimate_size with argument x. +Many proof harnesses follow this predictable format—to verify a function foo, we create arbitrary values for each of foo's arguments, then call foo on those arguments.

+

The autoharness subcommand leverages this observation to automatically generate and run harnesses. +Kani scans the crate for functions whose arguments all implement the kani::Arbitrary trait, generates harnesses for them, then runs them. +These harnesses are internal to Kani--i.e., Kani does not make any changes to your source code.

+

Usage

+

Run either:

+
# cargo kani autoharness -Z unstable-options
+
+

or

+
# kani autoharness -Z unstable-options <FILE>
+
+

If Kani detects that all of a function foo's arguments implement kani::Arbitrary, it will generate and run a #[kani::proof] harness, which prints:

+
Autoharness: Checking function foo against all possible inputs...
+<VERIFICATION RESULTS>
+
+

However, if Kani detects that foo has a contract, it will instead generate a #[kani::proof_for_contract] harness and verify the contract:

+
Autoharness: Checking function foo's contract against all possible inputs...
+<VERIFICATION RESULTS>
+
+

Kani generates and runs these harnesses internally—the user only sees the verification results.

+

The autoharness subcommand has options --include-function and --exclude-function to include and exclude particular functions. +These flags look for partial matches against the fully qualified name of a function.

+

For example, if a module my_module has many functions, but we are only interested in my_module::foo and my_module::bar, we can run:

+
cargo run autoharness -Z unstable-options --include-function foo include-function bar
+
+

To exclude my_module entirely, run:

+
cargo run autoharness -Z unstable-options --exclude-function my_module
+
+

Example

+

Using the estimate_size example from First Steps again:

+
fn estimate_size(x: u32) -> u32 {
+    if x < 256 {
+        if x < 128 {
+            return 1;
+        } else {
+            return 3;
+        }
+    } else if x < 1024 {
+        if x > 1022 {
+            panic!("Oh no, a failing corner case!");
+        } else {
+            return 5;
+        }
+    } else {
+        if x < 2048 {
+            return 7;
+        } else {
+            return 9;
+        }
+    }
+}
+
+

We get:

+
# cargo kani autoharness -Z unstable-options
+Autoharness: Checking function estimate_size against all possible inputs...
+RESULTS:
+Check 3: estimate_size.assertion.1
+         - Status: FAILURE
+         - Description: "Oh no, a failing corner case!"
+[...]
+
+Verification failed for - estimate_size
+Complete - 0 successfully verified functions, 1 failures, 1 total.
+
+

Request for comments

+

This feature is experimental and is therefore subject to change. +If you have ideas for improving the user experience of this feature, +please add them to this GitHub issue.

+

Limitations

+

Kani will only generate an automatic harness for a function if it can determine that all of the function's arguments implement Arbitrary. +It does not attempt to derive/implement Arbitrary for any types, even if those types could implement Arbitrary.

+

If a function contains a loop with a loop contract, Kani will detect the presence of a loop contract and verify that contract. +If, however, the loop does not have a contract, then there is currently no way to specify an unwinding bound for the function, meaning that Kani may hang as it tries to unwind the loop. +We recommend using the --exclude-function option to exclude any functions that have this issue (or --harness-timeout to bail after attempting verification for some amount of time).

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/reference/experimental/concrete-playback.html b/reference/experimental/concrete-playback.html new file mode 100644 index 000000000000..d89aa43f295b --- /dev/null +++ b/reference/experimental/concrete-playback.html @@ -0,0 +1,258 @@ + + + + + + Concrete Playback - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Concrete Playback

+

When the result of a certain check comes back as a FAILURE, Kani offers the concrete-playback option to help debug. This feature generates a Rust unit test case that plays back a failing proof harness using a concrete counterexample.

+

When concrete playback is enabled, Kani will generate unit tests for assertions that failed during verification, +as well as cover statements that are reachable.

+

These tests can then be executed using Kani's playback subcommand.

+

Usage

+

In order to enable this feature, run Kani with the -Z concrete-playback --concrete-playback=[print|inplace] flag. +After getting a verification failure, Kani will generate a Rust unit test case that plays back a failing +proof harness with a concrete counterexample. +The concrete playback modes mean the following:

+
    +
  • print: Kani will just print the unit test to stdout. +You will then need to copy this unit test into the same module as your proof harness. +This is also helpful if you just want to quickly find out which values were assigned by kani::any() calls.
  • +
  • inplace: Kani will automatically copy the unit test into your source code. +Before running this mode, you might find it helpful to have your existing code committed to git. +That way, you can easily remove the unit test with git revert. +Note that Kani will not copy the unit test into your source code if it detects +that the exact same test already exists.
  • +
+

After the unit test is in your source code, you can run it with the playback subcommand. +To debug it, there are a couple of options:

+ +

To manually compile and run the test, you can use Kani's playback subcommand:

+
cargo kani playback -Z concrete-playback -- ${unit_test_func_name}
+
+

The output from this command is similar to cargo test. +The output will have a line in the beginning like +Running unittests {files} ({binary}).

+

You can further debug the binary with tools like rust-gdb or lldb.

+

Example

+

Running kani -Z concrete-playback --concrete-playback=print on the following source file:

+
#[kani::proof]
+fn proof_harness() {
+    let a: u8 = kani::any();
+    let b: u16 = kani::any();
+    assert!(a / 2 * 2 == a &&
+            b / 2 * 2 == b);
+}
+
+

yields a concrete playback Rust unit test similar to the one below:

+
#[test]
+fn kani_concrete_playback_proof_harness_16220658101615121791() {
+    let concrete_vals: Vec<Vec<u8>> = vec![
+        // 133
+        vec![133],
+        // 35207
+        vec![135, 137],
+    ];
+    kani::concrete_playback_run(concrete_vals, proof_harness);
+}
+
+

Here, 133 and 35207 are the concrete values that, when substituted for a and b, +cause an assertion failure. +vec![135, 137] is the byte array representation of 35207.

+

Request for comments

+

This feature is experimental and is therefore subject to change. +If you have ideas for improving the user experience of this feature, +please add them to this GitHub issue. +We are tracking the existing feature requests in +this GitHub milestone.

+

Limitations

+
    +
  • This feature does not generate unit tests for failing non-panic checks (e.g., UB checks). +This is because checks would not trigger runtime errors during concrete playback. +Kani generates warning messages for this.
  • +
  • This feature does not support generating unit tests for multiple assertion failures within the same harness. +This limitation might be removed in the future. +Kani generates warning messages for this.
  • +
  • This feature requires that you use the same Kani version to generate the test and to playback. +Any extra compilation option used during verification must be used during playback.
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/reference/experimental/contracts.html b/reference/experimental/contracts.html new file mode 100644 index 000000000000..270971f5a7d2 --- /dev/null +++ b/reference/experimental/contracts.html @@ -0,0 +1,233 @@ + + + + + + Contracts - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Contracts

+

Consider the following example:

+
fn gcd(mut max: u8, mut min: u8) -> u8 {
+    if min > max {
+        std::mem::swap(&mut max, &mut min);
+    }
+
+    let rest = max % min;
+    if rest == 0 { min } else { gcd(min, rest) }
+}
+
+

Let's assume we want to verify some code that calls gcd. +In the worst case, the number of steps (recursions) in gcd approaches 1.5 times the number of bits needed to represent the input numbers. +So, for two large 64-bit numbers, a single call to gcd can take almost 96 iterations. +It would be very expensive for Kani to unroll each of these iterations and then perform symbolic execution.

+

Instead, we can write contracts with guarantees about gcd's behavior. +Once Kani verifies that gcd's contracts are correct, it can replace each invocation of gcd with its contracts, which reduces verification time for gcd's callers. +For example, perhaps we want to ensure that the returned result does indeed divide both max and min. +In that case, we could write contracts like these:

+
#[kani::requires(min != 0 && max != 0)]
+#[kani::ensures(|result| *result != 0 && max % *result == 0 && min % *result == 0)]
+#[kani::recursion]
+fn gcd(mut max: u8, mut min: u8) -> u8 { ... }
+
+

Since gcd performs max % min (and perhaps swaps those values), passing zero as an argument could cause a division by zero. +The requires contract tells Kani to restrict the range of nondeterministic inputs to nonzero ones so that we don't run into this error. +The ensures contract is what actually checks that the result is a correct divisor for the inputs. +(The recursion attribute is required when using contracts on recursive functions).

+

Then, we would write a harness to verify those contracts, like so:

+
#[kani::proof_for_contract(gcd)]
+fn check_gcd() {
+    let max: u8 = kani::any();
+    let min: u8 = kani::any();
+    gcd(max, min);
+}
+
+

and verify it by running kani -Z function-contracts.

+

Once Kani verifies the contracts, we can use Kani's stubbing feature to replace all invocations to gcd with its contracts, for instance:

+
// Assume foo() invokes gcd().
+// By using stub_verified, we tell Kani to replace 
+// invocations of gcd() with its verified contracts.
+#[kani::proof]
+#[kani::stub_verified(gcd)]
+fn check_foo() {
+    let x: u8 = kani::any();
+    foo(x);
+}
+
+

By leveraging the stubbing feature, we can replace the (expensive) gcd call with a verified abstraction of its behavior, greatly reducing verification time for foo.

+

There is far more to learn about contracts. +We highly recommend reading our blog post about contracts (from which this gcd example is taken). We also recommend looking at the contracts module in our documentation.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/reference/experimental/coverage.html b/reference/experimental/coverage.html new file mode 100644 index 000000000000..5a1cfcf49137 --- /dev/null +++ b/reference/experimental/coverage.html @@ -0,0 +1,209 @@ + + + + + + Coverage - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Coverage

+

Recall our estimate_size example from First steps, +where we wrote a proof harness constraining the range of inputs to integers less than 4096:

+
#[cfg(kani)]
+#[kani::proof]
+fn verify_success() {
+    let x: u32 = kani::any();
+    kani::assume(x < 4096);
+    let y = estimate_size(x);
+    assert!(y < 10);
+}
+
+

We must wonder if we've really fully tested our function. +What if we revise the function, but forget to update the assumption in our proof harness to cover the new range of inputs?

+

Fortunately, Kani is able to report a coverage metric for each proof harness. +In the first-steps-v2 directory, try running:

+
cargo kani --coverage -Z source-coverage --harness verify_success
+
+

which verifies the harness, then prints coverage information for each line. +In this case, we see that each line of estimate_size is followed by FULL, indicating that our proof harness provides full coverage.

+

Try changing the assumption in the proof harness to x < 2048. +Now the harness won't be testing all possible cases. +Rerun the command. +You'll see this line:

+
src/lib.rs, 24, NONE
+
+

which indicates that the proof no longer covers line 24, which addresses the case where x >= 2048.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/reference/experimental/experimental-features.html b/reference/experimental/experimental-features.html new file mode 100644 index 000000000000..115b17d4727d --- /dev/null +++ b/reference/experimental/experimental-features.html @@ -0,0 +1,186 @@ + + + + + + Experimental features - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Experimental Features

+

We elaborate on some of the more commonly used experimental features in Kani. +This is not an exhaustive list; to see all of Kani's experimental features, run cargo kani --help. +To use an experimental feature, invoke Kani with the --unstable or -Z flag followed by the name of the feature.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/reference/experimental/loop-contracts.html b/reference/experimental/loop-contracts.html new file mode 100644 index 000000000000..0b95e5514166 --- /dev/null +++ b/reference/experimental/loop-contracts.html @@ -0,0 +1,312 @@ + + + + + + Loop Contracts - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Loop Contracts

+

Loop contract are used to specify invariants for loops for the sake of extending Kani's bounded proofs to unbounded proofs. +A loop invariant is an expression that holds upon entering a loop and after every execution of the loop body. +It captures something that does not change about every step of the loop.

+

It is worth revisiting the discussion about bounded proof and +loop unwinding. In short, bounds on the number of times Kani unwinds loops also bound the size of inputs, +and hence result in a bounded proof. +Loop contracts are used to abstract out loops as non-loop blocks to avoid loop unwinding, and hence remove the bounds on the inputs.

+

Consider the following example:

+
fn simple_loop() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    while x > 1 {
+        x = x - 1;
+    }
+
+    assert!(x == 1);
+}
+
+

In this program, the loop repeatedly decrements x until it equals 1. Because we haven't specified an upper bound for x, to verify this function, +Kani needs to unwind the loop for u64::MAX iterations, which is computationally expensive. Loop contracts allow us to abstract the loop behavior, significantly reducing the verification cost.

+

With loop contracts, we can specify the loop’s behavior using invariants. For example:

+
#![feature(stmt_expr_attributes)]
+#![feature(proc_macro_hygiene)]
+
+fn simple_loop_with_loop_contracts() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    #[kani::loop_invariant(x >= 1)]
+    while x > 1 {
+        x = x - 1;
+    }
+
+    assert!(x == 1);
+}
+
+

Here, the loop invariant #[kani::loop_invariant(x >= 1)] specifies that the condition x >= 1 must hold true at the start of each iteration before the loop guard is +checked. Once Kani verifies that the loop invariant is inductive, it will use the invariant to abstract the loop and avoid unwinding.

+

Now let's run the proof with loop contracts through kani:

+
kani simple_loop_with_loop_contracts.rs  -Z loop-contracts
+
+

The output reported by Kani on the example will be

+
...
+
+
+Check 10: simple_loop_with_loop_contracts.loop_invariant_base.1
+         - Status: SUCCESS
+         - Description: "Check invariant before entry for loop simple_loop_with_loop_contracts.0"
+         - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts
+
+Check 11: simple_loop_with_loop_contracts.loop_assigns.1
+         - Status: SUCCESS
+         - Description: "Check assigns clause inclusion for loop simple_loop_with_loop_contracts.0"
+         - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts
+
+Check 13: simple_loop_with_loop_contracts.assigns.1
+         - Status: SUCCESS
+         - Description: "Check that x is assignable"
+         - Location: simple_while_loop.rs:17:9 in function simple_loop_with_loop_contracts
+
+Check 14: simple_loop_with_loop_contracts.loop_invariant_step.1
+         - Status: SUCCESS
+         - Description: "Check invariant after step for loop simple_loop_with_loop_contracts.0"
+         - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts
+
+Check 15: simple_loop_with_loop_contracts.loop_invariant_step.2
+         - Status: SUCCESS
+         - Description: "Check invariant after step for loop simple_loop_with_loop_contracts.0"
+         - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts
+
+...
+
+SUMMARY:
+ ** 0 of 99 failed
+
+VERIFICATION:- SUCCESSFUL
+Verification Time: 0.3897019s
+
+Complete - 1 successfully verified harnesses, 0 failures, 1 total.
+
+

Loop contracts for while loops

+

Syntax

+
+

#[kani::loop_invariant( Expression )]

+

while Expressionexcept struct expression BlockExpression

+
+

An invariant contract #[kani::loop_invariant(cond)] accepts a valid Boolean expression cond over the variables visible at the same scope as the loop.

+

Semantics

+

A loop invariant contract expands to several assumptions and assertions:

+
    +
  1. The invariant is asserted just before the first iteration.
  2. +
  3. The invariant is assumed on a non-deterministic state to model a non-deterministic iteration.
  4. +
  5. The invariant is finally asserted again to establish its inductiveness.
  6. +
+

Mathematical induction is the working principle here. (1) establishes the base case for induction, and (2) & (3) establish the inductive case. +Therefore, the invariant must hold after the loop execution for any number of iterations. The invariant, together with the negation of the loop guard, +must be sufficient to establish subsequent assertions. If it is not, the abstraction is too imprecise and the user must supply a stronger invariant.

+

To illustrate the key idea, we show how Kani abstracts the loop in simple_loop_with_loop_contracts as a non-loop block:

+
assert!(x >= 1) // check loop invariant for the base case.
+x = kani::any();
+kani::assume(x >= 1);
+if x > 1 {
+    // proof path 1:
+    //   both loop guard and loop invariant are satisfied.
+    x = x - 1;
+    assert!(x >= 1); // check that loop invariant is inductive.
+    kani::assume(false) // block this proof path.
+}
+// proof path 2:
+//   loop invariant is satisfied and loop guard is violated.
+assert!(x == 1);
+
+

That is, we assume that we are in an arbitrary iteration after checking that the loop invariant holds for the base case. With the inductive hypothesis (kani::assume(x >= 1);), +we will either enter the loop (proof path 1) or leave the loop (proof path 2). We prove the two paths separately by killing path 1 with kani::assume(false);. +Note that all assertions after kani::assume(false) will be ignored as false => p can be deduced as true for any p.

+

In proof path 1, we prove properties inside the loop and at last check that the loop invariant is inductive.

+

In proof path 2, we prove properties after leaving the loop. As we leave the loop only when the loop guard is violated, the post condition of the loop can be expressed as +!guard && inv, which is x <= 1 && x >= 1 in the example. The postcondition implies x == 1—the property we want to prove at the end of simple_loop_with_loop_contracts.

+

Limitations

+

Loop contracts comes with the following limitations.

+
    +
  1. Only while loops are supported. The other three kinds of loops are not supported: loop loops +, while let loops, and for loops.
  2. +
  3. Kani infers loop modifies with alias analysis. Loop modifies are those variables we assume to be arbitrary in the inductive hypothesis, and should cover all memory locations that are written to during +the execution of the loops. A proof will fail if the inferred loop modifies misses some targets written in the loops. +We observed this happens when some fields of structs are modified by some other functions called in the loops.
  4. +
  5. Kani doesn't check if a loop will always terminate in proofs with loop contracts. So it could be that some properties are proved successfully with Kani but actually are unreachable due to the +non-termination of some loops.
  6. +
  7. We don't check if loop invariants are side-effect free. A loop invariant with a side effect could lead to an unsound proof result. Make sure that the specified loop contracts are side-effect free.
  8. +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/reference/experimental/stubbing.html b/reference/experimental/stubbing.html new file mode 100644 index 000000000000..f0968479e652 --- /dev/null +++ b/reference/experimental/stubbing.html @@ -0,0 +1,353 @@ + + + + + + Stubbing - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Stubbing

+

Stubbing (or mocking) is an unstable feature which allows users to specify that certain items should be replaced with stubs (mocks) of those items during verification. +At present, the only items where stubbing can be applied are functions and methods (see limitations for more details).

+

When to consider stubbing

+

In general, we have identified three reasons where users may consider stubbing:

+
    +
  • Unsupported features: The code under verification contains features that Kani does not support, such as inline assembly.
  • +
  • Bad performance: The code under verification contains features that Kani supports, but it leads to bad verification performance (for example, deserialization code).
  • +
  • Compositional reasoning: The code under verification contains code that has been verified separately. +Stubbing the code that has already been verified with a less complex version that mimics its behavior can result in reduced verification workloads.
  • +
+

In most cases, stubbing enables users to verify code that otherwise would be impractical to verify. +Although definitions for mocking (normally used in testing) and stubbing may slightly differ depending on who you ask, we often use both terms interchangeably.

+

Components

+

The stubbing feature can be enabled by using the --enable-stubbing option when calling Kani. +Since it's an unstable feature, it requires passing the --enable-unstable option in addition to --enable-stubbing.

+

At present, the only component of the stubbing feature is the #[kani::stub(<original>, <replacement>)] attribute, +which allows you to specify the pair of functions/methods that must be stubbed in a harness.

+ +

The #[kani::stub(...)] attribute

+

The stub attribute #[kani::stub(<original>, <replacement>)] is the main tool of the stubbing feature.

+

It indicates to Kani that the function/method with name <original> should be replaced with the function/method with name <replacement> during the compilation step. +The names of these functions/methods are resolved using Rust's standard name resolution rules. +This includes support for imports like use foo::bar as baz, as well as imports of multiple versions of the same crate.

+

This attribute must be specified on a per-harness basis. This provides a high degree of flexibility for users, since they are given the option to stub the same item with different replacements (or not use stubbing at all) depending on the proof harness. In addition, the attribute can be specified multiple times per harness, so that multiple (non-conflicting) stub pairings are supported.

+

An example: stubbing random

+

Let's see a simple example where we use the rand::random function +to generate an encryption key.

+
#[cfg(kani)]
+#[kani::proof]
+fn encrypt_then_decrypt_is_identity() {
+    let data: u32 = kani::any();
+    let encryption_key: u32 = rand::random();
+    let encrypted_data = data ^ encryption_key;
+    let decrypted_data = encrypted_data ^ encryption_key;
+    assert_eq!(data, decrypted_data);
+}
+
+
+

At present, Kani fails to verify this example due to issue #1781.

+

However, we can work around this limitation thanks to the stubbing feature:

+
#[cfg(kani)]
+fn mock_random<T: kani::Arbitrary>() -> T {
+    kani::any()
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::stub(rand::random, mock_random)]
+fn encrypt_then_decrypt_is_identity() {
+    let data: u32 = kani::any();
+    let encryption_key: u32 = rand::random();
+    let encrypted_data = data ^ encryption_key;
+    let decrypted_data = encrypted_data ^ encryption_key;
+    assert_eq!(data, decrypted_data);
+}
+
+

Here, the #[kani::stub(rand::random, mock_random)] attribute indicates to Kani that it should replace rand::random with the stub mock_random. +Note that this is a fair assumption to do: rand::random is expected to return any u32 value, just like kani::any.

+

Now, let's run it through Kani:

+
cargo kani --enable-unstable --enable-stubbing --harness encrypt_then_decrypt_is_identity
+
+

The verification result is composed of a single check: the assertion corresponding to assert_eq!(data, decrypted_data).

+
RESULTS:
+Check 1: encrypt_then_decrypt_is_identity.assertion.1
+         - Status: SUCCESS
+         - Description: "assertion failed: data == decrypted_data"
+         - Location: src/main.rs:18:5 in function encrypt_then_decrypt_is_identity
+
+
+SUMMARY:
+ ** 0 of 1 failed
+
+VERIFICATION:- SUCCESSFUL
+
+

Kani shows that the assertion is successful, avoiding any issues that appear if we attempt to verify the code without stubbing.

+

Limitations

+

In the following, we describe all the limitations of the stubbing feature.

+

Usage restrictions

+

The usage of stubbing is limited to the verification of a single harness. +Therefore, users are required to pass the --harness option when using the stubbing feature.

+

In addition, this feature isn't compatible with concrete playback.

+

Support

+

Support for stubbing is currently limited to functions and methods. All other items aren't supported.

+

The following are examples of items that could be good candidates for stubbing, but aren't supported:

+
    +
  • Types
  • +
  • Macros
  • +
  • Traits
  • +
  • Intrinsics
  • +
+

We acknowledge that support for method stubbing isn't as ergonomic as it could be. +A common problem when attempting to define method stubs is that we don't have access to the private fields of an object (i.e., the fields in self). +One workaround is to use the unsafe function std::mem::transmute, as in this example:

+
struct Foo {
+    x: u32,
+}
+
+impl Foo {
+    pub fn m(&self) -> u32 {
+        0
+    }
+}
+
+struct MockFoo {
+    pub x: u32,
+}
+
+fn mock_m(foo: &Foo) -> u32 {
+    let mock: &MockFoo = unsafe { std::mem::transmute(foo) };
+    return mock.x;
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::stub(Foo::m, mock_m)]
+fn my_harness() { ... }
+
+

However, this isn't recommended since it's unsafe and error-prone. +In general, we don't recommend stubbing for private functions/methods. +Doing so can lead to brittle proofs: private functions/methods are subject to change or removal even in version minor upgrades (they aren't part of the APIs). +Therefore, proofs that rely on stubbing for private functions/methods might incur a high maintenance burden.

+

Error conditions

+

Given a set of original-replacement pairs, Kani will exit with an error if:

+
    +
  1. a specified original function does not exist;
  2. +
  3. a specified replacement stub does not exist;
  4. +
  5. the user specifies conflicting stubs for the same harness (e.g., if the same original function is mapped to multiple replacement functions); or
  6. +
  7. the signature of the replacement stub is not compatible with the signature of the original function/method (see next section).
  8. +
+

Stub compatibility and validation

+

We consider a stub and a function/method to be compatible if all the following conditions are met:

+
    +
  • They have the same number of parameters.
  • +
  • They have the same return type.
  • +
  • Each parameter in the stub has the same type as the corresponding parameter in the original function/method.
  • +
  • The stub must have the same number of generic parameters as the original function/method. +However, a generic parameter in the stub is allowed to have a different name than the corresponding parameter in the original function/method. +For example, the stub bar<A, B>(x: A, y: B) -> B is considered to have a type compatible with the function foo<S, T>(x: S, y: T) -> T.
  • +
  • The bounds for each type parameter don't need to match; however, all calls to the original function must also satisfy the bounds of the stub.
  • +
+

The final point is the most subtle. +We don't require that a type parameter in the signature of the stub implements the same traits as the corresponding type parameter in the signature of the original function/method. +However, Kani will reject a stub if a trait mismatch leads to a situation where a statically dispatched call to a trait method cannot be resolved during monomorphization. +For example, this restriction rules out the following harness:

+
fn foo<T>(_x: T) -> bool {
+    false
+}
+
+trait DoIt {
+    fn do_it(&self) -> bool;
+}
+
+fn bar<T: DoIt>(x: T) -> bool {
+    x.do_it()
+}
+
+#[kani::proof]
+#[kani::stub(foo, bar)]
+fn harness() {
+    assert!(foo("hello"));
+}
+
+

The call to the trait method DoIt::do_it is unresolvable in the stub bar when the type parameter T is instantiated with the type &str. +On the other hand, this approach provides some flexibility, such as allowing our earlier example of mocking rand::random: +both rand::random and my_random have type () -> T, but in the first case T is restricted such that the type Standard implements Distribution<T>, +whereas in the latter case T has to implement kani::Arbitrary. +This trait mismatch is allowed because at this call site T is instantiated with u32, which implements kani::Arbitrary.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/regression-testing.html b/regression-testing.html new file mode 100644 index 000000000000..e81e0e8f5249 --- /dev/null +++ b/regression-testing.html @@ -0,0 +1,362 @@ + + + + + + Regression testing - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Regression testing

+

Kani relies on a quite extensive range of tests to perform regression testing. +Regression testing can be executed by running the command:

+
./scripts/kani-regression.sh
+
+

The kani-regression.sh script executes different testing commands, which we classify into:

+ +

See below for a description of each one.

+

Note that regression testing is run whenever a Pull Request is opened, updated or merged +into the main branch. Therefore, it's a good idea to run regression testing locally before +submitting a Pull Request for Kani.

+

Kani testing suites

+

The Kani testing suites are the main testing resource for Kani. In most cases, the +tests contained in the Kani testing suites are single Rust files that are run +using the following command:

+
kani file.rs <options>
+
+

Command-line options can be passed to the test by adding a special +comment to the file. See testing options for more details.

+

In particular, the Kani testing suites are composed of:

+
    +
  • kani: The main testing suite for Kani. The test is a single Rust file that's +run through Kani. In general, the test passes if verification with Kani +is successful, otherwise it fails.
  • +
  • firecracker: Works like kani but contains tests inspired by +Firecracker code.
  • +
  • prusti: Works like kani but contains tests from the +Prusti tool.
  • +
  • smack: Works like kani but contains tests from the +SMACK tool.
  • +
  • kani-fixme: Similar to kani, but runs ignored tests from the kani testing +suite (i.e., tests with fixme or ignore in their name). +Allows us to detect when a previously not supported test becomes +supported. More details in "Fixme" tests.
  • +
  • expected: Similar to kani but with an additional check which ensures that +lines appearing in *.expected files appear in the output +generated by kani.
  • +
  • ui: Works like expected, but focuses on the user interface (e.g., +warnings) instead of the verification output.
  • +
  • cargo-kani: This suite is designed to test the cargo-kani command. As such, +this suite works with packages instead of single Rust files. +Arguments can be specified in the Cargo.toml configuration file. +Similar to the expected suite, we look for *.expected files +for each harness in the package.
  • +
  • cargo-ui: Similar to cargo-kani, but focuses on the user interface like the ui test suite.
  • +
  • script-based-pre: This suite is useful to execute script-based tests, and +also allows checking expected output and exit codes after +running them. The suite uses the exec mode, described in +more detail here.
  • +
+

We've extended +compiletest (the +Rust compiler testing framework) to work with these suites. That way, we take +advantage of all compiletest features (e.g., parallel execution).

+

Testing stages

+

The process of running single-file tests is split into three stages:

+
    +
  • check: This stage uses the Rust front-end to detect if the example is valid +Rust code.
  • +
  • codegen: This stage uses the Kani back-end to determine if we can generate +GotoC code.
  • +
  • verify: This stage uses CBMC to obtain a verification result.
  • +
+

If a test fails, the error message will include the stage where it failed:

+
error: test failed: expected check success, got failure
+
+

When working on a test that's expected to fail, there are two options to +indicate an expected failure. The first one is to add a comment

+
// kani-<stage>-fail
+
+

at the top of the test file, where <stage> is the stage where the test is +expected to fail.

+

The other option is to use the predicate kani::expect_fail(cond, message) +included in the Kani library. The cond in kani::expect_fail is a condition +that you expect not to hold during verification. The testing framework expects +one EXPECTED FAIL message in the verification output for each use of the +predicate.

+
+

NOTE: kani::expect_fail is only useful to indicate failure in the +verify stage, errors in other stages will be considered testing failures.

+
+

Testing options

+

Many tests will require passing command-line options to Kani. These options can +be specified in single Rust files by adding a comment at the top of the file:

+
// kani-flags: <options>
+
+

For example, to use an unwinding value of 4 in a test, we can write:

+
// kani-flags: --default-unwind 4
+
+

For cargo-kani tests, the preferred way to pass command-line options is adding +them to Cargo.toml. See Usage on a package for more details.

+

"Fixme" tests

+

Any test containing fixme or ignore in its name is considered a test not +supported for some reason (i.e., they return an unexpected verification result).

+

However, "fixme" tests included in the kani folder are run via the kani-fixme +testing suite. kani-fixme works on test files from kani but:

+
    +
  1. Only runs tests whose name contains fixme or ignore (ignoring the rest).
  2. +
  3. The expected outcome is failure. In other words, a test is successful if it +fails.
  4. +
+

We welcome contributions with "fixme" tests which demonstrate a bug or +unsupported feature in Kani. Ideally, the test should include some comments +regarding:

+
    +
  • The expected result of the test.
  • +
  • The actual result of the test (e.g., interesting parts of the output).
  • +
  • Links to related issues.
  • +
+

To include a new "fixme" test in kani you only need to ensure its name contains +fixme or ignore. If your changes to Kani cause a "fixme" test to become +supported, you only need to rename it so the name does not contain fixme nor +ignore.

+

Rust unit tests

+

These tests follow the +Rust unit testing +style.

+

At present, Kani runs unit tests from the following packages:

+
    +
  • cprover_bindings
  • +
  • kani-compiler
  • +
  • cargo-kani
  • +
+

Python unit tests

+

We use the Python unit testing framework to +test the CBMC JSON parser.

+

Script-based tests

+

These are tests which are run using scripts. Scripting gives us the ability to +perform ad-hoc checks that cannot be done otherwise. They are currently used +for:

+
    +
  • Standard library codegen
  • +
  • Firecracker virtio codegen
  • +
  • Diamond dependency
  • +
+

In fact, most of them are equivalent to running cargo kani and performing +checks on the output. The downside to scripting is that these tests will always +be run, even if there have not been any changes since the last time the +regression was run.

+
+

NOTE: With the addition of the exec mode for compiletest (described +below), we'll be migrating these script-based tests to other suites using the +exec mode. The exec mode allows us to take advantage of compiletest +features while executing script-based tests (e.g., parallel execution).

+
+

The exec mode

+

The exec mode in compiletest allows us to execute script-based tests, in +addition to checking expected output and exit codes after running them.

+

In particular, tests are expected to be placed directly under the test directory +(e.g., script-based-pre) in a directory with a config.yml file, which +should contain:

+
    +
  • script: The path to the script to be executed.
  • +
  • expected (optional): The path to the .expected file to +use for output comparison.
  • +
  • exit_code (optional): The exit code to be returned by executing +the script (a zero exit code is expected if not specified).
  • +
+

For example, let's say want to test the script exit-one.sh:

+
echo "Exiting with code 1!"
+exit 1
+
+

In this case, we'll create a folder that contains the config.yml file:

+
script: exit-one.sh
+expected: exit-one.expected
+exit_code: 1
+
+

where exit-one.expected is simply a text file such as:

+
Exiting with code 1!
+
+

If expected isn't specified, the output won't be checked. If exit_code isn't +specified, the exec mode will check the exit code was zero.

+

Note that all paths specified in the config.yml file are local to the test +directory, which is the working directory assumed when executing the test. This +is meant to avoid problems when executing the test manually.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/repo-crawl.html b/repo-crawl.html new file mode 100644 index 000000000000..3e80446e53c4 --- /dev/null +++ b/repo-crawl.html @@ -0,0 +1,269 @@ + + + + + + (Experimental) Testing with a Large Number of Repositories - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

(Experimental) Testing with a Large Number of Repositories

+

This section explains how to run Kani on a large number of crates +downloaded from git forges. You may want to do this if you are going +to test Kani's ability to handle Rust features found in projects out +in the wild.

+

For the first half, we will explain how to use data from crates.io to +pick targets. Second half will explain how to use a script to run on a +list of selected repositories.

+

Picking Repositories

+

In picking repositories, you may want to select by metrics like +popularity or by the presence of certain features. In this section, we +will explain how to select top ripostes by download count.

+

We will use the db-dump method of getting data from crates.io as it +is zero cost to their website and gives us SQL access. To start, have +the following programs set up on your computer.

+
    +
  • docker
  • +
  • docker-compose.
  • +
+
    +
  1. Start PostgreSQL. Paste in the following yaml file as +docker-compose.yaml. version: '3.3' may need to change.
  2. +
+
version: '3.3'
+services:
+  db:
+    image: postgres:latest
+    restart: always
+    environment:
+      - POSTGRES_USER=postgres
+      - POSTGRES_PASSWORD=postgres
+    volumes:
+      - crates-data:/var/lib/postgresql/data
+    logging:
+      driver: "json-file"
+      options:
+        max-size: "50m"
+volumes:
+  crates-data:
+    driver: local
+
+

Then, run the following to start the setup.

+
docker-compose up -d
+
+

Once set up, run docker ls to figure out the container's name. We +will refer to the name as $CONTAINER_NAME from now on.

+
    +
  1. +

    Download actual data from crates.io. First, run the following +command to get a shell in the container: docker exec -it --user postgres $CONTAINER_NAME bash. Now, run the following to grab and +install the data into the repository. Please note that this may +take a while.

    +
    wget https://static.crates.io/db-dump.tar.gz
    +tar -xf db-dump.tar.gz
    +psql postgres -f */schema.sql
    +psql postgres -f */import.sql
    +
    +
  2. +
  3. +

    Extract the data. In the same docker shell, run the following to +extract the top 1k repositories. Other SQL queries may be used if +you want another criteria

    +
    \copy
    +(SELECT name, repository, downloads  FROM crates
    +WHERE repository LIKE 'http%' ORDER BY DOWNLOADS DESC LIMIT 1000)
    +to 'top-1k.csv' csv header;
    +
    +
  4. +
  5. +

    Clean the data. The above query will capture duplicates paths that +are deeper than the repository. You can clean these out.

    +
      +
    • URL from CSV: cat top-1k.csv | awk -F ',' '{ print $2 }' | grep -v 'http.*'
    • +
    • Remove long paths: sed 's/tree\/master.*$//g'
    • +
    • Once processed, you can dedup with sort | uniq --unique
    • +
    +
  6. +
+

Running the List of Repositories

+

In this step we will download the list of repositories using a script +assess-scan-on-repos.sh

+

Make sure to have Kani ready to run. For that, see the build instructions.

+

From the repository root, you can run the script with +./scripts/exps/assess-scan-on-repos.sh $URL_LIST_FILE where +$URL_LIST_FILE points to a line-delimited list of URLs you want to +run Kani on. Repositories that give warnings or errors can be grepping +for with "STDERR Warnings" and "Error exit in" respectively.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/.nojekyll b/rfc/.nojekyll new file mode 100644 index 000000000000..f17311098f54 --- /dev/null +++ b/rfc/.nojekyll @@ -0,0 +1 @@ +This file makes sure that Github Pages doesn't process mdBook's output. diff --git a/rfc/404.html b/rfc/404.html new file mode 100644 index 000000000000..afb9c66021ca --- /dev/null +++ b/rfc/404.html @@ -0,0 +1,170 @@ + + + + + + Page not found - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Document not found (404)

+

This URL is invalid, sorry. Please use the navigation bar or search to continue.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/FontAwesome/css/font-awesome.css b/rfc/FontAwesome/css/font-awesome.css new file mode 100644 index 000000000000..540440ce89f2 --- /dev/null +++ b/rfc/FontAwesome/css/font-awesome.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/rfc/FontAwesome/fonts/FontAwesome.ttf b/rfc/FontAwesome/fonts/FontAwesome.ttf new file mode 100644 index 000000000000..35acda2fa119 Binary files /dev/null and b/rfc/FontAwesome/fonts/FontAwesome.ttf differ diff --git a/rfc/FontAwesome/fonts/fontawesome-webfont.eot b/rfc/FontAwesome/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000000..e9f60ca953f9 Binary files /dev/null and b/rfc/FontAwesome/fonts/fontawesome-webfont.eot differ diff --git a/rfc/FontAwesome/fonts/fontawesome-webfont.svg b/rfc/FontAwesome/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000000..855c845e538b --- /dev/null +++ b/rfc/FontAwesome/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rfc/FontAwesome/fonts/fontawesome-webfont.ttf b/rfc/FontAwesome/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000000..35acda2fa119 Binary files /dev/null and b/rfc/FontAwesome/fonts/fontawesome-webfont.ttf differ diff --git a/rfc/FontAwesome/fonts/fontawesome-webfont.woff b/rfc/FontAwesome/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000000..400014a4b06e Binary files /dev/null and b/rfc/FontAwesome/fonts/fontawesome-webfont.woff differ diff --git a/rfc/FontAwesome/fonts/fontawesome-webfont.woff2 b/rfc/FontAwesome/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000000..4d13fc60404b Binary files /dev/null and b/rfc/FontAwesome/fonts/fontawesome-webfont.woff2 differ diff --git a/rfc/ayu-highlight.css b/rfc/ayu-highlight.css new file mode 100644 index 000000000000..0c45c6f14608 --- /dev/null +++ b/rfc/ayu-highlight.css @@ -0,0 +1,79 @@ +/* +Based off of the Ayu theme +Original by Dempfi (https://github.com/dempfi/ayu) +*/ + +.hljs { + display: block; + overflow-x: auto; + background: #191f26; + color: #e6e1cf; + padding: 0.5em; +} + +.hljs-comment, +.hljs-quote { + color: #5c6773; + font-style: italic; +} + +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-attr, +.hljs-regexp, +.hljs-link, +.hljs-selector-id, +.hljs-selector-class { + color: #ff7733; +} + +.hljs-number, +.hljs-meta, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #ffee99; +} + +.hljs-string, +.hljs-bullet { + color: #b8cc52; +} + +.hljs-title, +.hljs-built_in, +.hljs-section { + color: #ffb454; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-symbol { + color: #ff7733; +} + +.hljs-name { + color: #36a3d9; +} + +.hljs-tag { + color: #00568d; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #91b362; +} + +.hljs-deletion { + color: #d96c75; +} diff --git a/rfc/book.js b/rfc/book.js new file mode 100644 index 000000000000..d40440c72317 --- /dev/null +++ b/rfc/book.js @@ -0,0 +1,679 @@ +"use strict"; + +// Fix back button cache problem +window.onunload = function () { }; + +// Global variable, shared between modules +function playground_text(playground) { + let code_block = playground.querySelector("code"); + + if (window.ace && code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + return editor.getValue(); + } else { + return code_block.textContent; + } +} + +(function codeSnippets() { + function fetch_with_timeout(url, options, timeout = 6000) { + return Promise.race([ + fetch(url, options), + new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)) + ]); + } + + var playgrounds = Array.from(document.querySelectorAll(".playground")); + if (playgrounds.length > 0) { + fetch_with_timeout("https://play.rust-lang.org/meta/crates", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + }) + .then(response => response.json()) + .then(response => { + // get list of crates available in the rust playground + let playground_crates = response.crates.map(item => item["id"]); + playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); + }); + } + + function handle_crate_list_update(playground_block, playground_crates) { + // update the play buttons after receiving the response + update_play_button(playground_block, playground_crates); + + // and install on change listener to dynamically update ACE editors + if (window.ace) { + let code_block = playground_block.querySelector("code"); + if (code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + editor.addEventListener("change", function (e) { + update_play_button(playground_block, playground_crates); + }); + // add Ctrl-Enter command to execute rust code + editor.commands.addCommand({ + name: "run", + bindKey: { + win: "Ctrl-Enter", + mac: "Ctrl-Enter" + }, + exec: _editor => run_rust_code(playground_block) + }); + } + } + } + + // updates the visibility of play button based on `no_run` class and + // used crates vs ones available on http://play.rust-lang.org + function update_play_button(pre_block, playground_crates) { + var play_button = pre_block.querySelector(".play-button"); + + // skip if code is `no_run` + if (pre_block.querySelector('code').classList.contains("no_run")) { + play_button.classList.add("hidden"); + return; + } + + // get list of `extern crate`'s from snippet + var txt = playground_text(pre_block); + var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g; + var snippet_crates = []; + var item; + while (item = re.exec(txt)) { + snippet_crates.push(item[1]); + } + + // check if all used crates are available on play.rust-lang.org + var all_available = snippet_crates.every(function (elem) { + return playground_crates.indexOf(elem) > -1; + }); + + if (all_available) { + play_button.classList.remove("hidden"); + } else { + play_button.classList.add("hidden"); + } + } + + function run_rust_code(code_block) { + var result_block = code_block.querySelector(".result"); + if (!result_block) { + result_block = document.createElement('code'); + result_block.className = 'result hljs language-bash'; + + code_block.append(result_block); + } + + let text = playground_text(code_block); + let classes = code_block.querySelector('code').classList; + let edition = "2015"; + if(classes.contains("edition2018")) { + edition = "2018"; + } else if(classes.contains("edition2021")) { + edition = "2021"; + } + var params = { + version: "stable", + optimize: "0", + code: text, + edition: edition + }; + + if (text.indexOf("#![feature") !== -1) { + params.version = "nightly"; + } + + result_block.innerText = "Running..."; + + fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params) + }) + .then(response => response.json()) + .then(response => { + if (response.result.trim() === '') { + result_block.innerText = "No output"; + result_block.classList.add("result-no-output"); + } else { + result_block.innerText = response.result; + result_block.classList.remove("result-no-output"); + } + }) + .catch(error => result_block.innerText = "Playground Communication: " + error.message); + } + + // Syntax highlighting Configuration + hljs.configure({ + tabReplace: ' ', // 4 spaces + languages: [], // Languages used for auto-detection + }); + + let code_nodes = Array + .from(document.querySelectorAll('code')) + // Don't highlight `inline code` blocks in headers. + .filter(function (node) {return !node.parentElement.classList.contains("header"); }); + + if (window.ace) { + // language-rust class needs to be removed for editable + // blocks or highlightjs will capture events + code_nodes + .filter(function (node) {return node.classList.contains("editable"); }) + .forEach(function (block) { block.classList.remove('language-rust'); }); + + Array + code_nodes + .filter(function (node) {return !node.classList.contains("editable"); }) + .forEach(function (block) { hljs.highlightBlock(block); }); + } else { + code_nodes.forEach(function (block) { hljs.highlightBlock(block); }); + } + + // Adding the hljs class gives code blocks the color css + // even if highlighting doesn't apply + code_nodes.forEach(function (block) { block.classList.add('hljs'); }); + + Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { + + var lines = Array.from(block.querySelectorAll('.boring')); + // If no lines were hidden, return + if (!lines.length) { return; } + block.classList.add("hide-boring"); + + var buttons = document.createElement('div'); + buttons.className = 'buttons'; + buttons.innerHTML = ""; + + // add expand button + var pre_block = block.parentNode; + pre_block.insertBefore(buttons, pre_block.firstChild); + + pre_block.querySelector('.buttons').addEventListener('click', function (e) { + if (e.target.classList.contains('fa-eye')) { + e.target.classList.remove('fa-eye'); + e.target.classList.add('fa-eye-slash'); + e.target.title = 'Hide lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.remove('hide-boring'); + } else if (e.target.classList.contains('fa-eye-slash')) { + e.target.classList.remove('fa-eye-slash'); + e.target.classList.add('fa-eye'); + e.target.title = 'Show hidden lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.add('hide-boring'); + } + }); + }); + + if (window.playground_copyable) { + Array.from(document.querySelectorAll('pre code')).forEach(function (block) { + var pre_block = block.parentNode; + if (!pre_block.classList.contains('playground')) { + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var clipButton = document.createElement('button'); + clipButton.className = 'fa fa-copy clip-button'; + clipButton.title = 'Copy to clipboard'; + clipButton.setAttribute('aria-label', clipButton.title); + clipButton.innerHTML = ''; + + buttons.insertBefore(clipButton, buttons.firstChild); + } + }); + } + + // Process playground code blocks + Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) { + // Add play button + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var runCodeButton = document.createElement('button'); + runCodeButton.className = 'fa fa-play play-button'; + runCodeButton.hidden = true; + runCodeButton.title = 'Run this code'; + runCodeButton.setAttribute('aria-label', runCodeButton.title); + + buttons.insertBefore(runCodeButton, buttons.firstChild); + runCodeButton.addEventListener('click', function (e) { + run_rust_code(pre_block); + }); + + if (window.playground_copyable) { + var copyCodeClipboardButton = document.createElement('button'); + copyCodeClipboardButton.className = 'fa fa-copy clip-button'; + copyCodeClipboardButton.innerHTML = ''; + copyCodeClipboardButton.title = 'Copy to clipboard'; + copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title); + + buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild); + } + + let code_block = pre_block.querySelector("code"); + if (window.ace && code_block.classList.contains("editable")) { + var undoChangesButton = document.createElement('button'); + undoChangesButton.className = 'fa fa-history reset-button'; + undoChangesButton.title = 'Undo changes'; + undoChangesButton.setAttribute('aria-label', undoChangesButton.title); + + buttons.insertBefore(undoChangesButton, buttons.firstChild); + + undoChangesButton.addEventListener('click', function () { + let editor = window.ace.edit(code_block); + editor.setValue(editor.originalCode); + editor.clearSelection(); + }); + } + }); +})(); + +(function themes() { + var html = document.querySelector('html'); + var themeToggleButton = document.getElementById('theme-toggle'); + var themePopup = document.getElementById('theme-list'); + var themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); + var stylesheets = { + ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"), + tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"), + highlight: document.querySelector("[href$='highlight.css']"), + }; + + function showThemes() { + themePopup.style.display = 'block'; + themeToggleButton.setAttribute('aria-expanded', true); + themePopup.querySelector("button#" + get_theme()).focus(); + } + + function hideThemes() { + themePopup.style.display = 'none'; + themeToggleButton.setAttribute('aria-expanded', false); + themeToggleButton.focus(); + } + + function get_theme() { + var theme; + try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { } + if (theme === null || theme === undefined) { + return default_theme; + } else { + return theme; + } + } + + function set_theme(theme, store = true) { + let ace_theme; + + if (theme == 'coal' || theme == 'navy') { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = false; + stylesheets.highlight.disabled = true; + + ace_theme = "ace/theme/tomorrow_night"; + } else if (theme == 'ayu') { + stylesheets.ayuHighlight.disabled = false; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = true; + ace_theme = "ace/theme/tomorrow_night"; + } else { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = false; + ace_theme = "ace/theme/dawn"; + } + + setTimeout(function () { + themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor; + }, 1); + + if (window.ace && window.editors) { + window.editors.forEach(function (editor) { + editor.setTheme(ace_theme); + }); + } + + var previousTheme = get_theme(); + + if (store) { + try { localStorage.setItem('mdbook-theme', theme); } catch (e) { } + } + + html.classList.remove(previousTheme); + html.classList.add(theme); + } + + // Set theme + var theme = get_theme(); + + set_theme(theme, false); + + themeToggleButton.addEventListener('click', function () { + if (themePopup.style.display === 'block') { + hideThemes(); + } else { + showThemes(); + } + }); + + themePopup.addEventListener('click', function (e) { + var theme; + if (e.target.className === "theme") { + theme = e.target.id; + } else if (e.target.parentElement.className === "theme") { + theme = e.target.parentElement.id; + } else { + return; + } + set_theme(theme); + }); + + themePopup.addEventListener('focusout', function(e) { + // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) + if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) { + hideThemes(); + } + }); + + // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 + document.addEventListener('click', function(e) { + if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { + hideThemes(); + } + }); + + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (!themePopup.contains(e.target)) { return; } + + switch (e.key) { + case 'Escape': + e.preventDefault(); + hideThemes(); + break; + case 'ArrowUp': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.previousElementSibling) { + li.previousElementSibling.querySelector('button').focus(); + } + break; + case 'ArrowDown': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.nextElementSibling) { + li.nextElementSibling.querySelector('button').focus(); + } + break; + case 'Home': + e.preventDefault(); + themePopup.querySelector('li:first-child button').focus(); + break; + case 'End': + e.preventDefault(); + themePopup.querySelector('li:last-child button').focus(); + break; + } + }); +})(); + +(function sidebar() { + var html = document.querySelector("html"); + var sidebar = document.getElementById("sidebar"); + var sidebarLinks = document.querySelectorAll('#sidebar a'); + var sidebarToggleButton = document.getElementById("sidebar-toggle"); + var sidebarResizeHandle = document.getElementById("sidebar-resize-handle"); + var firstContact = null; + + function showSidebar() { + html.classList.remove('sidebar-hidden') + html.classList.add('sidebar-visible'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', 0); + }); + sidebarToggleButton.setAttribute('aria-expanded', true); + sidebar.setAttribute('aria-hidden', false); + try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { } + } + + + var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle'); + + function toggleSection(ev) { + ev.currentTarget.parentElement.classList.toggle('expanded'); + } + + Array.from(sidebarAnchorToggles).forEach(function (el) { + el.addEventListener('click', toggleSection); + }); + + function hideSidebar() { + html.classList.remove('sidebar-visible') + html.classList.add('sidebar-hidden'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', -1); + }); + sidebarToggleButton.setAttribute('aria-expanded', false); + sidebar.setAttribute('aria-hidden', true); + try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { } + } + + // Toggle sidebar + sidebarToggleButton.addEventListener('click', function sidebarToggle() { + if (html.classList.contains("sidebar-hidden")) { + var current_width = parseInt( + document.documentElement.style.getPropertyValue('--sidebar-width'), 10); + if (current_width < 150) { + document.documentElement.style.setProperty('--sidebar-width', '150px'); + } + showSidebar(); + } else if (html.classList.contains("sidebar-visible")) { + hideSidebar(); + } else { + if (getComputedStyle(sidebar)['transform'] === 'none') { + hideSidebar(); + } else { + showSidebar(); + } + } + }); + + sidebarResizeHandle.addEventListener('mousedown', initResize, false); + + function initResize(e) { + window.addEventListener('mousemove', resize, false); + window.addEventListener('mouseup', stopResize, false); + html.classList.add('sidebar-resizing'); + } + function resize(e) { + var pos = (e.clientX - sidebar.offsetLeft); + if (pos < 20) { + hideSidebar(); + } else { + if (html.classList.contains("sidebar-hidden")) { + showSidebar(); + } + pos = Math.min(pos, window.innerWidth - 100); + document.documentElement.style.setProperty('--sidebar-width', pos + 'px'); + } + } + //on mouseup remove windows functions mousemove & mouseup + function stopResize(e) { + html.classList.remove('sidebar-resizing'); + window.removeEventListener('mousemove', resize, false); + window.removeEventListener('mouseup', stopResize, false); + } + + document.addEventListener('touchstart', function (e) { + firstContact = { + x: e.touches[0].clientX, + time: Date.now() + }; + }, { passive: true }); + + document.addEventListener('touchmove', function (e) { + if (!firstContact) + return; + + var curX = e.touches[0].clientX; + var xDiff = curX - firstContact.x, + tDiff = Date.now() - firstContact.time; + + if (tDiff < 250 && Math.abs(xDiff) >= 150) { + if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) + showSidebar(); + else if (xDiff < 0 && curX < 300) + hideSidebar(); + + firstContact = null; + } + }, { passive: true }); + + // Scroll sidebar to current active section + var activeSection = document.getElementById("sidebar").querySelector(".active"); + if (activeSection) { + // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView + activeSection.scrollIntoView({ block: 'center' }); + } +})(); + +(function chapterNavigation() { + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (window.search && window.search.hasFocus()) { return; } + + switch (e.key) { + case 'ArrowRight': + e.preventDefault(); + var nextButton = document.querySelector('.nav-chapters.next'); + if (nextButton) { + window.location.href = nextButton.href; + } + break; + case 'ArrowLeft': + e.preventDefault(); + var previousButton = document.querySelector('.nav-chapters.previous'); + if (previousButton) { + window.location.href = previousButton.href; + } + break; + } + }); +})(); + +(function clipboard() { + var clipButtons = document.querySelectorAll('.clip-button'); + + function hideTooltip(elem) { + elem.firstChild.innerText = ""; + elem.className = 'fa fa-copy clip-button'; + } + + function showTooltip(elem, msg) { + elem.firstChild.innerText = msg; + elem.className = 'fa fa-copy tooltipped'; + } + + var clipboardSnippets = new ClipboardJS('.clip-button', { + text: function (trigger) { + hideTooltip(trigger); + let playground = trigger.closest("pre"); + return playground_text(playground); + } + }); + + Array.from(clipButtons).forEach(function (clipButton) { + clipButton.addEventListener('mouseout', function (e) { + hideTooltip(e.currentTarget); + }); + }); + + clipboardSnippets.on('success', function (e) { + e.clearSelection(); + showTooltip(e.trigger, "Copied!"); + }); + + clipboardSnippets.on('error', function (e) { + showTooltip(e.trigger, "Clipboard error!"); + }); +})(); + +(function scrollToTop () { + var menuTitle = document.querySelector('.menu-title'); + + menuTitle.addEventListener('click', function () { + document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); + }); +})(); + +(function controllMenu() { + var menu = document.getElementById('menu-bar'); + + (function controllPosition() { + var scrollTop = document.scrollingElement.scrollTop; + var prevScrollTop = scrollTop; + var minMenuY = -menu.clientHeight - 50; + // When the script loads, the page can be at any scroll (e.g. if you reforesh it). + menu.style.top = scrollTop + 'px'; + // Same as parseInt(menu.style.top.slice(0, -2), but faster + var topCache = menu.style.top.slice(0, -2); + menu.classList.remove('sticky'); + var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster + document.addEventListener('scroll', function () { + scrollTop = Math.max(document.scrollingElement.scrollTop, 0); + // `null` means that it doesn't need to be updated + var nextSticky = null; + var nextTop = null; + var scrollDown = scrollTop > prevScrollTop; + var menuPosAbsoluteY = topCache - scrollTop; + if (scrollDown) { + nextSticky = false; + if (menuPosAbsoluteY > 0) { + nextTop = prevScrollTop; + } + } else { + if (menuPosAbsoluteY > 0) { + nextSticky = true; + } else if (menuPosAbsoluteY < minMenuY) { + nextTop = prevScrollTop + minMenuY; + } + } + if (nextSticky === true && stickyCache === false) { + menu.classList.add('sticky'); + stickyCache = true; + } else if (nextSticky === false && stickyCache === true) { + menu.classList.remove('sticky'); + stickyCache = false; + } + if (nextTop !== null) { + menu.style.top = nextTop + 'px'; + topCache = nextTop; + } + prevScrollTop = scrollTop; + }, { passive: true }); + })(); + (function controllBorder() { + menu.classList.remove('bordered'); + document.addEventListener('scroll', function () { + if (menu.offsetTop === 0) { + menu.classList.remove('bordered'); + } else { + menu.classList.add('bordered'); + } + }, { passive: true }); + })(); +})(); diff --git a/rfc/clipboard.min.js b/rfc/clipboard.min.js new file mode 100644 index 000000000000..02c549e35c86 --- /dev/null +++ b/rfc/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.4 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n .hljs { + color: var(--links); +} + +/* Menu Bar */ + +#menu-bar, +#menu-bar-hover-placeholder { + z-index: 101; + margin: auto calc(0px - var(--page-padding)); +} +#menu-bar { + position: relative; + display: flex; + flex-wrap: wrap; + background-color: var(--bg); + border-bottom-color: var(--bg); + border-bottom-width: 1px; + border-bottom-style: solid; +} +#menu-bar.sticky, +.js #menu-bar-hover-placeholder:hover + #menu-bar, +.js #menu-bar:hover, +.js.sidebar-visible #menu-bar { + position: -webkit-sticky; + position: sticky; + top: 0 !important; +} +#menu-bar-hover-placeholder { + position: sticky; + position: -webkit-sticky; + top: 0; + height: var(--menu-bar-height); +} +#menu-bar.bordered { + border-bottom-color: var(--table-border-color); +} +#menu-bar i, #menu-bar .icon-button { + position: relative; + padding: 0 8px; + z-index: 10; + line-height: var(--menu-bar-height); + cursor: pointer; + transition: color 0.5s; +} +@media only screen and (max-width: 420px) { + #menu-bar i, #menu-bar .icon-button { + padding: 0 5px; + } +} + +.icon-button { + border: none; + background: none; + padding: 0; + color: inherit; +} +.icon-button i { + margin: 0; +} + +.right-buttons { + margin: 0 15px; +} +.right-buttons a { + text-decoration: none; +} + +.left-buttons { + display: flex; + margin: 0 5px; +} +.no-js .left-buttons { + display: none; +} + +.menu-title { + display: inline-block; + font-weight: 200; + font-size: 2.4rem; + line-height: var(--menu-bar-height); + text-align: center; + margin: 0; + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.js .menu-title { + cursor: pointer; +} + +.menu-bar, +.menu-bar:visited, +.nav-chapters, +.nav-chapters:visited, +.mobile-nav-chapters, +.mobile-nav-chapters:visited, +.menu-bar .icon-button, +.menu-bar a i { + color: var(--icons); +} + +.menu-bar i:hover, +.menu-bar .icon-button:hover, +.nav-chapters:hover, +.mobile-nav-chapters i:hover { + color: var(--icons-hover); +} + +/* Nav Icons */ + +.nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + + position: fixed; + top: 0; + bottom: 0; + margin: 0; + max-width: 150px; + min-width: 90px; + + display: flex; + justify-content: center; + align-content: center; + flex-direction: column; + + transition: color 0.5s, background-color 0.5s; +} + +.nav-chapters:hover { + text-decoration: none; + background-color: var(--theme-hover); + transition: background-color 0.15s, color 0.15s; +} + +.nav-wrapper { + margin-top: 50px; + display: none; +} + +.mobile-nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + width: 90px; + border-radius: 5px; + background-color: var(--sidebar-bg); +} + +.previous { + float: left; +} + +.next { + float: right; + right: var(--page-padding); +} + +@media only screen and (max-width: 1080px) { + .nav-wide-wrapper { display: none; } + .nav-wrapper { display: block; } +} + +@media only screen and (max-width: 1380px) { + .sidebar-visible .nav-wide-wrapper { display: none; } + .sidebar-visible .nav-wrapper { display: block; } +} + +/* Inline code */ + +:not(pre) > .hljs { + display: inline; + padding: 0.1em 0.3em; + border-radius: 3px; +} + +:not(pre):not(a) > .hljs { + color: var(--inline-code-color); + overflow-x: initial; +} + +a:hover > .hljs { + text-decoration: underline; +} + +pre { + position: relative; +} +pre > .buttons { + position: absolute; + z-index: 100; + right: 5px; + top: 5px; + + color: var(--sidebar-fg); + cursor: pointer; +} +pre > .buttons :hover { + color: var(--sidebar-active); +} +pre > .buttons i { + margin-left: 8px; +} +pre > .buttons button { + color: inherit; + background: transparent; + border: none; + cursor: inherit; +} +pre > .result { + margin-top: 10px; +} + +/* Search */ + +#searchresults a { + text-decoration: none; +} + +mark { + border-radius: 2px; + padding: 0 3px 1px 3px; + margin: 0 -3px -1px -3px; + background-color: var(--search-mark-bg); + transition: background-color 300ms linear; + cursor: pointer; +} + +mark.fade-out { + background-color: rgba(0,0,0,0) !important; + cursor: auto; +} + +.searchbar-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} + +#searchbar { + width: 100%; + margin: 5px auto 0px auto; + padding: 10px 16px; + transition: box-shadow 300ms ease-in-out; + border: 1px solid var(--searchbar-border-color); + border-radius: 3px; + background-color: var(--searchbar-bg); + color: var(--searchbar-fg); +} +#searchbar:focus, +#searchbar.active { + box-shadow: 0 0 3px var(--searchbar-shadow-color); +} + +.searchresults-header { + font-weight: bold; + font-size: 1em; + padding: 18px 0 0 5px; + color: var(--searchresults-header-fg); +} + +.searchresults-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); + border-bottom: 1px dashed var(--searchresults-border-color); +} + +ul#searchresults { + list-style: none; + padding-left: 20px; +} +ul#searchresults li { + margin: 10px 0px; + padding: 2px; + border-radius: 2px; +} +ul#searchresults li.focus { + background-color: var(--searchresults-li-bg); +} +ul#searchresults span.teaser { + display: block; + clear: both; + margin: 5px 0 0 20px; + font-size: 0.8em; +} +ul#searchresults span.teaser em { + font-weight: bold; + font-style: normal; +} + +/* Sidebar */ + +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: var(--sidebar-width); + font-size: 0.875em; + box-sizing: border-box; + -webkit-overflow-scrolling: touch; + overscroll-behavior-y: contain; + background-color: var(--sidebar-bg); + color: var(--sidebar-fg); +} +.sidebar-resizing { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +.js:not(.sidebar-resizing) .sidebar { + transition: transform 0.3s; /* Animation: slide away */ +} +.sidebar code { + line-height: 2em; +} +.sidebar .sidebar-scrollbox { + overflow-y: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + padding: 10px 10px; +} +.sidebar .sidebar-resize-handle { + position: absolute; + cursor: col-resize; + width: 0; + right: 0; + top: 0; + bottom: 0; +} +.js .sidebar .sidebar-resize-handle { + cursor: col-resize; + width: 5px; +} +.sidebar-hidden .sidebar { + transform: translateX(calc(0px - var(--sidebar-width))); +} +.sidebar::-webkit-scrollbar { + background: var(--sidebar-bg); +} +.sidebar::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} + +.sidebar-visible .page-wrapper { + transform: translateX(var(--sidebar-width)); +} +@media only screen and (min-width: 620px) { + .sidebar-visible .page-wrapper { + transform: none; + margin-left: var(--sidebar-width); + } +} + +.chapter { + list-style: none outside none; + padding-left: 0; + line-height: 2.2em; +} + +.chapter ol { + width: 100%; +} + +.chapter li { + display: flex; + color: var(--sidebar-non-existant); +} +.chapter li a { + display: block; + padding: 0; + text-decoration: none; + color: var(--sidebar-fg); +} + +.chapter li a:hover { + color: var(--sidebar-active); +} + +.chapter li a.active { + color: var(--sidebar-active); +} + +.chapter li > a.toggle { + cursor: pointer; + display: block; + margin-left: auto; + padding: 0 10px; + user-select: none; + opacity: 0.68; +} + +.chapter li > a.toggle div { + transition: transform 0.5s; +} + +/* collapse the section */ +.chapter li:not(.expanded) + li > ol { + display: none; +} + +.chapter li.chapter-item { + line-height: 1.5em; + margin-top: 0.6em; +} + +.chapter li.expanded > a.toggle div { + transform: rotate(90deg); +} + +.spacer { + width: 100%; + height: 3px; + margin: 5px 0px; +} +.chapter .spacer { + background-color: var(--sidebar-spacer); +} + +@media (-moz-touch-enabled: 1), (pointer: coarse) { + .chapter li a { padding: 5px 0; } + .spacer { margin: 10px 0; } +} + +.section { + list-style: none outside none; + padding-left: 20px; + line-height: 1.9em; +} + +/* Theme Menu Popup */ + +.theme-popup { + position: absolute; + left: 10px; + top: var(--menu-bar-height); + z-index: 1000; + border-radius: 4px; + font-size: 0.7em; + color: var(--fg); + background: var(--theme-popup-bg); + border: 1px solid var(--theme-popup-border); + margin: 0; + padding: 0; + list-style: none; + display: none; +} +.theme-popup .default { + color: var(--icons); +} +.theme-popup .theme { + width: 100%; + border: 0; + margin: 0; + padding: 2px 10px; + line-height: 25px; + white-space: nowrap; + text-align: left; + cursor: pointer; + color: inherit; + background: inherit; + font-size: inherit; +} +.theme-popup .theme:hover { + background-color: var(--theme-hover); +} +.theme-popup .theme:hover:first-child, +.theme-popup .theme:hover:last-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; +} diff --git a/rfc/css/general.css b/rfc/css/general.css new file mode 100644 index 000000000000..ef2ba5048917 --- /dev/null +++ b/rfc/css/general.css @@ -0,0 +1,182 @@ +/* Base styles and content styles */ + +@import 'variables.css'; + +:root { + /* Browser default font-size is 16px, this way 1 rem = 10px */ + font-size: 62.5%; +} + +html { + font-family: "Open Sans", sans-serif; + color: var(--fg); + background-color: var(--bg); + text-size-adjust: none; + -webkit-text-size-adjust: none; +} + +body { + margin: 0; + font-size: 1.6rem; + overflow-x: hidden; +} + +code { + font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important; + font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ +} + +/* Don't change font size in headers. */ +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + font-size: unset; +} + +.left { float: left; } +.right { float: right; } +.boring { opacity: 0.6; } +.hide-boring .boring { display: none; } +.hidden { display: none !important; } + +h2, h3 { margin-top: 2.5em; } +h4, h5 { margin-top: 2em; } + +.header + .header h3, +.header + .header h4, +.header + .header h5 { + margin-top: 1em; +} + +h1:target::before, +h2:target::before, +h3:target::before, +h4:target::before, +h5:target::before, +h6:target::before { + display: inline-block; + content: "»"; + margin-left: -30px; + width: 30px; +} + +/* This is broken on Safari as of version 14, but is fixed + in Safari Technology Preview 117 which I think will be Safari 14.2. + https://bugs.webkit.org/show_bug.cgi?id=218076 +*/ +:target { + scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); +} + +.page { + outline: 0; + padding: 0 var(--page-padding); + margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ +} +.page-wrapper { + box-sizing: border-box; +} +.js:not(.sidebar-resizing) .page-wrapper { + transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ +} + +.content { + overflow-y: auto; + padding: 0 15px; + padding-bottom: 50px; +} +.content main { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} +.content p { line-height: 1.45em; } +.content ol { line-height: 1.45em; } +.content ul { line-height: 1.45em; } +.content a { text-decoration: none; } +.content a:hover { text-decoration: underline; } +.content img, .content video { max-width: 100%; } +.content .header:link, +.content .header:visited { + color: var(--fg); +} +.content .header:link, +.content .header:visited:hover { + text-decoration: none; +} + +table { + margin: 0 auto; + border-collapse: collapse; +} +table td { + padding: 3px 20px; + border: 1px var(--table-border-color) solid; +} +table thead { + background: var(--table-header-bg); +} +table thead td { + font-weight: 700; + border: none; +} +table thead th { + padding: 3px 20px; +} +table thead tr { + border: 1px var(--table-header-bg) solid; +} +/* Alternate background colors for rows */ +table tbody tr:nth-child(2n) { + background: var(--table-alternate-bg); +} + + +blockquote { + margin: 20px 0; + padding: 0 20px; + color: var(--fg); + background-color: var(--quote-bg); + border-top: .1em solid var(--quote-border); + border-bottom: .1em solid var(--quote-border); +} + + +:not(.footnote-definition) + .footnote-definition, +.footnote-definition + :not(.footnote-definition) { + margin-top: 2em; +} +.footnote-definition { + font-size: 0.9em; + margin: 0.5em 0; +} +.footnote-definition p { + display: inline; +} + +.tooltiptext { + position: absolute; + visibility: hidden; + color: #fff; + background-color: #333; + transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ + left: -8px; /* Half of the width of the icon */ + top: -35px; + font-size: 0.8em; + text-align: center; + border-radius: 6px; + padding: 5px 8px; + margin: 5px; + z-index: 1000; +} +.tooltipped .tooltiptext { + visibility: visible; +} + +.chapter li.part-title { + color: var(--sidebar-fg); + margin: 5px 0px; + font-weight: bold; +} + +.result-no-output { + font-style: italic; +} diff --git a/rfc/css/print.css b/rfc/css/print.css new file mode 100644 index 000000000000..5e690f755994 --- /dev/null +++ b/rfc/css/print.css @@ -0,0 +1,54 @@ + +#sidebar, +#menu-bar, +.nav-chapters, +.mobile-nav-chapters { + display: none; +} + +#page-wrapper.page-wrapper { + transform: none; + margin-left: 0px; + overflow-y: initial; +} + +#content { + max-width: none; + margin: 0; + padding: 0; +} + +.page { + overflow-y: initial; +} + +code { + background-color: #666666; + border-radius: 5px; + + /* Force background to be printed in Chrome */ + -webkit-print-color-adjust: exact; +} + +pre > .buttons { + z-index: 2; +} + +a, a:visited, a:active, a:hover { + color: #4183c4; + text-decoration: none; +} + +h1, h2, h3, h4, h5, h6 { + page-break-inside: avoid; + page-break-after: avoid; +} + +pre, code { + page-break-inside: avoid; + white-space: pre-wrap; +} + +.fa { + display: none !important; +} diff --git a/rfc/css/variables.css b/rfc/css/variables.css new file mode 100644 index 000000000000..56b634bc3766 --- /dev/null +++ b/rfc/css/variables.css @@ -0,0 +1,253 @@ + +/* Globals */ + +:root { + --sidebar-width: 300px; + --page-padding: 15px; + --content-max-width: 750px; + --menu-bar-height: 50px; +} + +/* Themes */ + +.ayu { + --bg: hsl(210, 25%, 8%); + --fg: #c5c5c5; + + --sidebar-bg: #14191f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #5c6773; + --sidebar-active: #ffb454; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #0096cf; + + --inline-code-color: #ffb454; + + --theme-popup-bg: #14191f; + --theme-popup-border: #5c6773; + --theme-hover: #191f26; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(210, 25%, 13%); + --table-header-bg: hsl(210, 25%, 28%); + --table-alternate-bg: hsl(210, 25%, 11%); + + --searchbar-border-color: #848484; + --searchbar-bg: #424242; + --searchbar-fg: #fff; + --searchbar-shadow-color: #d4c89f; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #252932; + --search-mark-bg: #e3b171; +} + +.coal { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; +} + +.light { + --bg: hsl(0, 0%, 100%); + --fg: hsl(0, 0%, 0%); + + --sidebar-bg: #fafafa; + --sidebar-fg: hsl(0, 0%, 0%); + --sidebar-non-existant: #aaaaaa; + --sidebar-active: #1f1fff; + --sidebar-spacer: #f4f4f4; + + --scrollbar: #8F8F8F; + + --icons: #747474; + --icons-hover: #000000; + + --links: #20609f; + + --inline-code-color: #301900; + + --theme-popup-bg: #fafafa; + --theme-popup-border: #cccccc; + --theme-hover: #e6e6e6; + + --quote-bg: hsl(197, 37%, 96%); + --quote-border: hsl(197, 37%, 91%); + + --table-border-color: hsl(0, 0%, 95%); + --table-header-bg: hsl(0, 0%, 80%); + --table-alternate-bg: hsl(0, 0%, 97%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #e4f2fe; + --search-mark-bg: #a2cff5; +} + +.navy { + --bg: hsl(226, 23%, 11%); + --fg: #bcbdd0; + + --sidebar-bg: #282d3f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505274; + --sidebar-active: #2b79a2; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #161923; + --theme-popup-border: #737480; + --theme-hover: #282e40; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(226, 23%, 16%); + --table-header-bg: hsl(226, 23%, 31%); + --table-alternate-bg: hsl(226, 23%, 14%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #aeaec6; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #5f5f71; + --searchresults-border-color: #5c5c68; + --searchresults-li-bg: #242430; + --search-mark-bg: #a2cff5; +} + +.rust { + --bg: hsl(60, 9%, 87%); + --fg: #262625; + + --sidebar-bg: #3b2e2a; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505254; + --sidebar-active: #e69f67; + --sidebar-spacer: #45373a; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #262625; + + --links: #2b79a2; + + --inline-code-color: #6e6b5e; + + --theme-popup-bg: #e1e1db; + --theme-popup-border: #b38f6b; + --theme-hover: #99908a; + + --quote-bg: hsl(60, 5%, 75%); + --quote-border: hsl(60, 5%, 70%); + + --table-border-color: hsl(60, 9%, 82%); + --table-header-bg: #b3a497; + --table-alternate-bg: hsl(60, 9%, 84%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #dec2a2; + --search-mark-bg: #e69f67; +} + +@media (prefers-color-scheme: dark) { + .light.no-js { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; + } +} diff --git a/rfc/elasticlunr.min.js b/rfc/elasticlunr.min.js new file mode 100644 index 000000000000..94b20dd2ef46 --- /dev/null +++ b/rfc/elasticlunr.min.js @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5 + * + * Copyright (C) 2017 Oliver Nightingale + * Copyright (C) 2017 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o + + + + diff --git a/rfc/fonts/OPEN-SANS-LICENSE.txt b/rfc/fonts/OPEN-SANS-LICENSE.txt new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/rfc/fonts/OPEN-SANS-LICENSE.txt @@ -0,0 +1,202 @@ + + 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/rfc/fonts/SOURCE-CODE-PRO-LICENSE.txt b/rfc/fonts/SOURCE-CODE-PRO-LICENSE.txt new file mode 100644 index 000000000000..366206f54950 --- /dev/null +++ b/rfc/fonts/SOURCE-CODE-PRO-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/rfc/fonts/fonts.css b/rfc/fonts/fonts.css new file mode 100644 index 000000000000..858efa59800b --- /dev/null +++ b/rfc/fonts/fonts.css @@ -0,0 +1,100 @@ +/* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */ +/* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */ + +/* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + src: local('Open Sans Light'), local('OpenSans-Light'), + url('open-sans-v17-all-charsets-300.woff2') format('woff2'); +} + +/* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), + url('open-sans-v17-all-charsets-300italic.woff2') format('woff2'); +} + +/* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans Regular'), local('OpenSans-Regular'), + url('open-sans-v17-all-charsets-regular.woff2') format('woff2'); +} + +/* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), + url('open-sans-v17-all-charsets-italic.woff2') format('woff2'); +} + +/* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), + url('open-sans-v17-all-charsets-600.woff2') format('woff2'); +} + +/* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), + url('open-sans-v17-all-charsets-600italic.woff2') format('woff2'); +} + +/* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), + url('open-sans-v17-all-charsets-700.woff2') format('woff2'); +} + +/* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), + url('open-sans-v17-all-charsets-700italic.woff2') format('woff2'); +} + +/* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 800; + src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), + url('open-sans-v17-all-charsets-800.woff2') format('woff2'); +} + +/* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 800; + src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), + url('open-sans-v17-all-charsets-800italic.woff2') format('woff2'); +} + +/* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 500; + src: url('source-code-pro-v11-all-charsets-500.woff2') format('woff2'); +} diff --git a/rfc/fonts/open-sans-v17-all-charsets-300.woff2 b/rfc/fonts/open-sans-v17-all-charsets-300.woff2 new file mode 100644 index 000000000000..9f51be370fa9 Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-300.woff2 differ diff --git a/rfc/fonts/open-sans-v17-all-charsets-300italic.woff2 b/rfc/fonts/open-sans-v17-all-charsets-300italic.woff2 new file mode 100644 index 000000000000..2f545448418c Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-300italic.woff2 differ diff --git a/rfc/fonts/open-sans-v17-all-charsets-600.woff2 b/rfc/fonts/open-sans-v17-all-charsets-600.woff2 new file mode 100644 index 000000000000..f503d558d58d Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-600.woff2 differ diff --git a/rfc/fonts/open-sans-v17-all-charsets-600italic.woff2 b/rfc/fonts/open-sans-v17-all-charsets-600italic.woff2 new file mode 100644 index 000000000000..c99aabe80340 Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-600italic.woff2 differ diff --git a/rfc/fonts/open-sans-v17-all-charsets-700.woff2 b/rfc/fonts/open-sans-v17-all-charsets-700.woff2 new file mode 100644 index 000000000000..421a1ab25fa8 Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-700.woff2 differ diff --git a/rfc/fonts/open-sans-v17-all-charsets-700italic.woff2 b/rfc/fonts/open-sans-v17-all-charsets-700italic.woff2 new file mode 100644 index 000000000000..12ce3d20d1ce Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-700italic.woff2 differ diff --git a/rfc/fonts/open-sans-v17-all-charsets-800.woff2 b/rfc/fonts/open-sans-v17-all-charsets-800.woff2 new file mode 100644 index 000000000000..c94a223b0332 Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-800.woff2 differ diff --git a/rfc/fonts/open-sans-v17-all-charsets-800italic.woff2 b/rfc/fonts/open-sans-v17-all-charsets-800italic.woff2 new file mode 100644 index 000000000000..eed7d3c63d1f Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-800italic.woff2 differ diff --git a/rfc/fonts/open-sans-v17-all-charsets-italic.woff2 b/rfc/fonts/open-sans-v17-all-charsets-italic.woff2 new file mode 100644 index 000000000000..398b68a0853f Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-italic.woff2 differ diff --git a/rfc/fonts/open-sans-v17-all-charsets-regular.woff2 b/rfc/fonts/open-sans-v17-all-charsets-regular.woff2 new file mode 100644 index 000000000000..8383e94c6547 Binary files /dev/null and b/rfc/fonts/open-sans-v17-all-charsets-regular.woff2 differ diff --git a/rfc/fonts/source-code-pro-v11-all-charsets-500.woff2 b/rfc/fonts/source-code-pro-v11-all-charsets-500.woff2 new file mode 100644 index 000000000000..722245682f59 Binary files /dev/null and b/rfc/fonts/source-code-pro-v11-all-charsets-500.woff2 differ diff --git a/rfc/highlight.css b/rfc/highlight.css new file mode 100644 index 000000000000..c2343227201e --- /dev/null +++ b/rfc/highlight.css @@ -0,0 +1,83 @@ +/* + * An increased contrast highlighting scheme loosely based on the + * "Base16 Atelier Dune Light" theme by Bram de Haan + * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) + * Original Base16 color scheme by Chris Kempson + * (https://github.com/chriskempson/base16) + */ + +/* Comment */ +.hljs-comment, +.hljs-quote { + color: #575757; +} + +/* Red */ +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-tag, +.hljs-name, +.hljs-regexp, +.hljs-link, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class { + color: #d70025; +} + +/* Orange */ +.hljs-number, +.hljs-meta, +.hljs-built_in, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #b21e00; +} + +/* Green */ +.hljs-string, +.hljs-symbol, +.hljs-bullet { + color: #008200; +} + +/* Blue */ +.hljs-title, +.hljs-section { + color: #0030f2; +} + +/* Purple */ +.hljs-keyword, +.hljs-selector-tag { + color: #9d00ec; +} + +.hljs { + display: block; + overflow-x: auto; + background: #f6f7f6; + color: #000; + padding: 0.5em; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #22863a; + background-color: #f0fff4; +} + +.hljs-deletion { + color: #b31d28; + background-color: #ffeef0; +} diff --git a/rfc/highlight.js b/rfc/highlight.js new file mode 100644 index 000000000000..180385b7028f --- /dev/null +++ b/rfc/highlight.js @@ -0,0 +1,6 @@ +/* + Highlight.js 10.1.1 (93fd0d73) + License: BSD-3-Clause + Copyright (c) 2006-2020, Ivan Sagalaev +*/ +var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="
",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("php",function(){"use strict";return function(e){var r={begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{aliases:["php","php3","php4","php5","php6","php7"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler"}),{className:"string",begin:/<<<['"]?\w+['"]?$/,end:/^\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]}]},t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},a,n]}}}());hljs.registerLanguage("nginx",function(){"use strict";return function(e){var n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/}/},{begin:"[\\$\\@]"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{$pattern:"[a-z/_]+",literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+"\\s+{",returnBegin:!0,end:"{",contains:[{className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|{",returnBegin:!0,contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:"[^\\s\\}]"}}}());hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}());hljs.registerLanguage("perl",function(){"use strict";return function(e){var n={$pattern:/[\w.]+/,keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when"},t={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:n},s={begin:"->{",end:"}"},r={variants:[{begin:/\$\d/},{begin:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{begin:/[\$%@][^\s\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT("^\\=\\w","\\=cut",{endsWithParent:!0}),s,{className:"string",contains:i,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*\\<",end:"\\>",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:"{\\w+}",contains:[],relevance:0},{begin:"-?\\w+\\s*\\=\\>",contains:[],relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",begin:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",relevance:10},{className:"regexp",begin:"(m|qr)?/",end:"/[a-z]*",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return t.contains=a,s.contains=a,{name:"Perl",aliases:["pl","pm"],keywords:n,contains:a}}}());hljs.registerLanguage("swift",function(){"use strict";return function(e){var i={keyword:"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c compactMap contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},n=e.COMMENT("/\\*","\\*/",{contains:["self"]}),t={className:"subst",begin:/\\\(/,end:"\\)",keywords:i,contains:[]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},r={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};return t.contains=[r],{name:"Swift",keywords:i,contains:[a,e.C_LINE_COMMENT_MODE,n,{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*[!?]"},{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*",relevance:0},r,{className:"function",beginKeywords:"func",end:"{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin://},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:i,contains:["self",r,a,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:i,end:"\\{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)\\b"},{beginKeywords:"import",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}());hljs.registerLanguage("makefile",function(){"use strict";return function(e){var i={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:")",end:">",keywords:{name:"style"},contains:[c],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}());hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b-?[a-z\._]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}());hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},i={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},c=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}());hljs.registerLanguage("coffeescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((e=>n=>!e.includes(n))(["var","const","let","function","static"])).join(" "),literal:n.concat(["yes","no","on","off"]).join(" "),built_in:a.concat(["npm","print"]).join(" ")},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:t,contains:["self"].concat(o)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/,contains:o.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[c,l]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[l]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[c]},c]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}());hljs.registerLanguage("ruby",function(){"use strict";return function(e){var n="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",a={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},i={begin:"#<",end:">"},r=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^\\=begin","^\\=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],c={className:"subst",begin:"#\\{",end:"}",keywords:a},t={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<[-~]?'?(\w+)(?:.|\n)*?\n\s*\1\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},b={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:a},d=[t,i,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(r)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[t,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:a},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[i,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\s*=>/,starts:{end:"$",contains:d}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:d}}];return{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:a,illegal:/\/\*/,contains:r.concat(g).concat(d)}}}());hljs.registerLanguage("yaml",function(){"use strict";return function(e){var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*\\'()[\\]]+",s={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]},i=e.inherit(s,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={begin:"{",end:"}",contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"\\-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b"},t,g,s],c=[...b];return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml","YAML"],contains:b}}}());hljs.registerLanguage("d",function(){"use strict";return function(e){var a={$pattern:e.UNDERSCORE_IDENT_RE,keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},d="((0|[1-9][\\d_]*)|0[bB][01_]+|0[xX]([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))",n="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",t={className:"number",begin:"\\b"+d+"(L|u|U|Lu|LU|uL|UL)?",relevance:0},_={className:"number",begin:"\\b(((0[xX](([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)\\.([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)|\\.?([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))[pP][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))|((0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(\\.\\d*|([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)))|\\d+\\.(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)|\\.(0|[1-9][\\d_]*)([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))?))([fF]|L|i|[fF]i|Li)?|"+d+"(i|[fF]i|Li))",relevance:0},r={className:"string",begin:"'("+n+"|.)",end:"'",illegal:"."},i={className:"string",begin:'"',contains:[{begin:n,relevance:0}],end:'"[cwd]?'},s=e.COMMENT("\\/\\+","\\+\\/",{contains:["self"],relevance:10});return{name:"D",keywords:a,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{className:"string",begin:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',relevance:10},i,{className:"string",begin:'[rq]"',end:'"[cwd]?',relevance:5},{className:"string",begin:"`",end:"`[cwd]?"},{className:"string",begin:'q"\\{',end:'\\}"'},_,t,r,{className:"meta",begin:"^#!",end:"$",relevance:5},{className:"meta",begin:"#(line)",end:"$",relevance:5},{className:"keyword",begin:"@[a-zA-Z_][a-zA-Z_\\d]*"}]}}}());hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}());hljs.registerLanguage("http",function(){"use strict";return function(e){var n="HTTP/[0-9\\.]+";return{name:"HTTP",aliases:["https"],illegal:"\\S",contains:[{begin:"^"+n,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+n+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:n},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}}());hljs.registerLanguage("haskell",function(){"use strict";return function(e){var n={variants:[e.COMMENT("--","$"),e.COMMENT("{-","-}",{contains:["self"]})]},i={className:"meta",begin:"{-#",end:"#-}"},a={className:"meta",begin:"^#",end:"$"},s={className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},l={begin:"\\(",end:"\\)",illegal:'"',contains:[i,a,{className:"type",begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TITLE_MODE,{begin:"[_a-z][\\w']*"}),n]};return{name:"Haskell",aliases:["hs"],keywords:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",contains:[{beginKeywords:"module",end:"where",keywords:"module where",contains:[l,n],illegal:"\\W\\.|;"},{begin:"\\bimport\\b",end:"$",keywords:"import qualified as hiding",contains:[l,n],illegal:"\\W\\.|;"},{className:"class",begin:"^(\\s*)?(class|instance)\\b",end:"where",keywords:"class family instance where",contains:[s,l,n]},{className:"class",begin:"\\b(data|(new)?type)\\b",end:"$",keywords:"data family type newtype deriving",contains:[i,s,l,{begin:"{",end:"}",contains:l.contains},n]},{beginKeywords:"default",end:"$",contains:[s,l,n]},{beginKeywords:"infix infixl infixr",end:"$",contains:[e.C_NUMBER_MODE,n]},{begin:"\\bforeign\\b",end:"$",keywords:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",contains:[s,e.QUOTE_STRING_MODE,n]},{className:"meta",begin:"#!\\/usr\\/bin\\/env runhaskell",end:"$"},i,a,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,s,e.inherit(e.TITLE_MODE,{begin:"^[_a-z][\\w']*"}),n,{begin:"->|<-"}]}}}());hljs.registerLanguage("handlebars",function(){"use strict";function e(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(n){const a={"builtin-name":"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield"},t=/\[.*?\]/,s=/[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/,i=e("(",/'.*?'/,"|",/".*?"/,"|",t,"|",s,"|",/\.|\//,")+"),r=e("(",t,"|",s,")(?==)"),l={begin:i,lexemes:/[\w.\/]+/},c=n.inherit(l,{keywords:{literal:"true false undefined null"}}),o={begin:/\(/,end:/\)/},m={className:"attr",begin:r,relevance:0,starts:{begin:/=/,end:/=/,starts:{contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,c,o]}}},d={contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,{begin:/as\s+\|/,keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]},m,c,o],returnEnd:!0},g=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/\)/})});o.contains=[g];const u=n.inherit(l,{keywords:a,className:"name",starts:n.inherit(d,{end:/}}/})}),b=n.inherit(l,{keywords:a,className:"name"}),h=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/}}/})});return{name:"Handlebars",aliases:["hbs","html.hbs","html.handlebars","htmlbars"],case_insensitive:!0,subLanguage:"xml",contains:[{begin:/\\\{\{/,skip:!0},{begin:/\\\\(?=\{\{)/,skip:!0},n.COMMENT(/\{\{!--/,/--\}\}/),n.COMMENT(/\{\{!/,/\}\}/),{className:"template-tag",begin:/\{\{\{\{(?!\/)/,end:/\}\}\}\}/,contains:[u],starts:{end:/\{\{\{\{\//,returnEnd:!0,subLanguage:"xml"}},{className:"template-tag",begin:/\{\{\{\{\//,end:/\}\}\}\}/,contains:[b]},{className:"template-tag",begin:/\{\{#/,end:/\}\}/,contains:[u]},{className:"template-tag",begin:/\{\{(?=else\}\})/,end:/\}\}/,keywords:"else"},{className:"template-tag",begin:/\{\{\//,end:/\}\}/,contains:[b]},{className:"template-variable",begin:/\{\{\{/,end:/\}\}\}/,contains:[h]},{className:"template-variable",begin:/\{\{/,end:/\}\}/,contains:[h]}]}}}());hljs.registerLanguage("rust",function(){"use strict";return function(e){var n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield",literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}}());hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}());hljs.registerLanguage("ini",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(...n){return n.map(n=>e(n)).join("")}return function(a){var s={className:"number",relevance:0,variants:[{begin:/([\+\-]+)?[\d]+_[\d_]+/},{begin:a.NUMBER_RE}]},i=a.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var t={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={className:"string",contains:[a.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,s,"self"],relevance:0},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map(n=>e(n)).join("|")+")";return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr",starts:{end:/$/,contains:[i,c,r,t,l,s]}}]}}}());hljs.registerLanguage("objectivec",function(){"use strict";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n,keyword:"@interface @class @protocol @implementation"};return{name:"Objective-C",aliases:["mm","objc","obj-c"],keywords:{$pattern:n,keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:"({|$)",excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}());hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}());hljs.registerLanguage("java",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(e){return a("(",e,")?")}function a(...n){return n.map(n=>e(n)).join("")}function s(...n){return"("+n.map(n=>e(n)).join("|")+")"}return function(e){var t="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",i={className:"meta",begin:"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},r=e=>a("[",e,"]+([",e,"_]*[",e,"]+)?"),c={className:"number",variants:[{begin:`\\b(0[bB]${r("01")})[lL]?`},{begin:`\\b(0${r("0-7")})[dDfFlL]?`},{begin:a(/\b0[xX]/,s(a(r("a-fA-F0-9"),/\./,r("a-fA-F0-9")),a(r("a-fA-F0-9"),/\.?/),a(/\./,r("a-fA-F0-9"))),/([pP][+-]?(\d+))?/,/[fFdDlL]?/)},{begin:a(/\b/,s(a(/\d*\./,r("\\d")),r("\\d")),/[eE][+-]?[\d]+[dDfF]?/)},{begin:a(/\b/,r(/\d/),n(/\.?/),n(r(/\d/)),/[dDfFlL]?/)}],relevance:0};return{name:"Java",aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\s*,\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c,i]}}}());hljs.registerLanguage("x86asm",function(){"use strict";return function(s){return{name:"Intel x86 Assembly",case_insensitive:!0,keywords:{$pattern:"[.%]?"+s.IDENT_RE,keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63",built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr",meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},contains:[s.COMMENT(";","$",{relevance:0}),{className:"number",variants:[{begin:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",relevance:0},{begin:"\\$[0-9][0-9A-Fa-f]*",relevance:0},{begin:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{begin:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"}]},s.QUOTE_STRING_MODE,{className:"string",variants:[{begin:"'",end:"[^\\\\]'"},{begin:"`",end:"[^\\\\]`"}],relevance:0},{className:"symbol",variants:[{begin:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{begin:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],relevance:0},{className:"subst",begin:"%[0-9]+",relevance:0},{className:"subst",begin:"%!S+",relevance:0},{className:"meta",begin:/^\s*\.[\w_-]+/}]}}}());hljs.registerLanguage("kotlin",function(){"use strict";return function(e){var n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:"\\${",end:"}",contains:[e.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},t={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(t,{className:"meta-string"})]}]},c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},a,r,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0}]}}}());hljs.registerLanguage("armasm",function(){"use strict";return function(s){const e={variants:[s.COMMENT("^[ \\t]*(?=#)","$",{relevance:0,excludeBegin:!0}),s.COMMENT("[;@]","$",{relevance:0}),s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE]};return{name:"ARM Assembly",case_insensitive:!0,aliases:["arm"],keywords:{$pattern:"\\.?"+s.IDENT_RE,meta:".2byte .4byte .align .ascii .asciz .balign .byte .code .data .else .end .endif .endm .endr .equ .err .exitm .extern .global .hword .if .ifdef .ifndef .include .irp .long .macro .rept .req .section .set .skip .space .text .word .arm .thumb .code16 .code32 .force_thumb .thumb_func .ltorg ALIAS ALIGN ARM AREA ASSERT ATTR CN CODE CODE16 CODE32 COMMON CP DATA DCB DCD DCDU DCDO DCFD DCFDU DCI DCQ DCQU DCW DCWU DN ELIF ELSE END ENDFUNC ENDIF ENDP ENTRY EQU EXPORT EXPORTAS EXTERN FIELD FILL FUNCTION GBLA GBLL GBLS GET GLOBAL IF IMPORT INCBIN INCLUDE INFO KEEP LCLA LCLL LCLS LTORG MACRO MAP MEND MEXIT NOFP OPT PRESERVE8 PROC QN READONLY RELOC REQUIRE REQUIRE8 RLIST FN ROUT SETA SETL SETS SN SPACE SUBT THUMB THUMBX TTL WHILE WEND ",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 pc lr sp ip sl sb fp a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8 f0 f1 f2 f3 f4 f5 f6 f7 p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 cpsr_c cpsr_x cpsr_s cpsr_f cpsr_cx cpsr_cxs cpsr_xs cpsr_xsf cpsr_sf cpsr_cxsf spsr_c spsr_x spsr_s spsr_f spsr_cx spsr_cxs spsr_xs spsr_xsf spsr_sf spsr_cxsf s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 {PC} {VAR} {TRUE} {FALSE} {OPT} {CONFIG} {ENDIAN} {CODESIZE} {CPU} {FPU} {ARCHITECTURE} {PCSTOREOFFSET} {ARMASM_VERSION} {INTER} {ROPI} {RWPI} {SWST} {NOSWST} . @"},contains:[{className:"keyword",begin:"\\b(adc|(qd?|sh?|u[qh]?)?add(8|16)?|usada?8|(q|sh?|u[qh]?)?(as|sa)x|and|adrl?|sbc|rs[bc]|asr|b[lx]?|blx|bxj|cbn?z|tb[bh]|bic|bfc|bfi|[su]bfx|bkpt|cdp2?|clz|clrex|cmp|cmn|cpsi[ed]|cps|setend|dbg|dmb|dsb|eor|isb|it[te]{0,3}|lsl|lsr|ror|rrx|ldm(([id][ab])|f[ds])?|ldr((s|ex)?[bhd])?|movt?|mvn|mra|mar|mul|[us]mull|smul[bwt][bt]|smu[as]d|smmul|smmla|mla|umlaal|smlal?([wbt][bt]|d)|mls|smlsl?[ds]|smc|svc|sev|mia([bt]{2}|ph)?|mrr?c2?|mcrr2?|mrs|msr|orr|orn|pkh(tb|bt)|rbit|rev(16|sh)?|sel|[su]sat(16)?|nop|pop|push|rfe([id][ab])?|stm([id][ab])?|str(ex)?[bhd]?|(qd?)?sub|(sh?|q|u[qh]?)?sub(8|16)|[su]xt(a?h|a?b(16)?)|srs([id][ab])?|swpb?|swi|smi|tst|teq|wfe|wfi|yield)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo)?[sptrx]?(?=\\s)"},e,s.QUOTE_STRING_MODE,{className:"string",begin:"'",end:"[^\\\\]'",relevance:0},{className:"title",begin:"\\|",end:"\\|",illegal:"\\n",relevance:0},{className:"number",variants:[{begin:"[#$=]?0x[0-9a-f]+"},{begin:"[#$=]?0b[01]+"},{begin:"[#$=]\\d+"},{begin:"\\b\\d+"}],relevance:0},{className:"symbol",variants:[{begin:"^[ \\t]*[a-z_\\.\\$][a-z0-9_\\.\\$]+:"},{begin:"^[a-z_\\.\\$][a-z0-9_\\.\\$]+"},{begin:"[=#]\\w+"}],relevance:0}]}}}());hljs.registerLanguage("go",function(){"use strict";return function(e){var n={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{name:"Go",aliases:["golang"],keywords:n,illegal:">>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}());hljs.registerLanguage("shell",function(){"use strict";return function(s){return{name:"Shell Session",aliases:["console"],contains:[{className:"meta",begin:"^\\s{0,3}[/\\w\\d\\[\\]()@-]*[>%$#]",starts:{end:"$",subLanguage:"bash"}}]}}}());hljs.registerLanguage("scala",function(){"use strict";return function(e){var n={className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"},{begin:"\\${",end:"}"}]},a={className:"string",variants:[{begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:'"""',end:'"""',relevance:10},{begin:'[a-z]+"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n]},{className:"string",begin:'[a-z]+"""',end:'"""',contains:[n],relevance:10}]},s={className:"type",begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={className:"title",begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,relevance:0},i={className:"class",beginKeywords:"class object trait type",end:/[:={\[\n;]/,excludeEnd:!0,contains:[{beginKeywords:"extends with",relevance:10},{begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},t]},l={className:"function",beginKeywords:"def",end:/[:={\[(\n;]/,excludeEnd:!0,contains:[t]};return{name:"Scala",keywords:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,{className:"symbol",begin:"'\\w[\\w\\d_]*(?!')"},s,l,i,e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"}]}}}());hljs.registerLanguage("julia",function(){"use strict";return function(e){var r="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",t={$pattern:r,keyword:"in isa where baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import importall let local macro module quote return true try using while type immutable abstract bitstype typealias ",literal:"true false ARGS C_NULL DevNull ENDIAN_BOM ENV I Inf Inf16 Inf32 Inf64 InsertionSort JULIA_HOME LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp STDERR STDIN STDOUT VERSION catalan e|0 eu|0 eulergamma golden im nothing pi γ π φ ",built_in:"ANY AbstractArray AbstractChannel AbstractFloat AbstractMatrix AbstractRNG AbstractSerializer AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError Associative Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError BufferStream CachingPool CapturedException CartesianIndex CartesianRange Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong ClusterManager Cmd CodeInfo Colon Complex Complex128 Complex32 Complex64 CompositeException Condition ConjArray ConjMatrix ConjVector Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring DataType Date DateFormat DateTime DenseArray DenseMatrix DenseVecOrMat DenseVector Diagonal Dict DimensionMismatch Dims DirectIndexString Display DivideError DomainError EOFError EachLine Enum Enumerate ErrorException Exception ExponentialBackOff Expr Factorization FileMonitor Float16 Float32 Float64 Function Future GlobalRef GotoNode HTML Hermitian IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException InvalidStateException Irrational KeyError LabelNode LinSpace LineNumberNode LoadError LowerTriangular MIME Matrix MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode NullException Nullable Number ObjectIdDict OrdinalRange OutOfMemoryError OverflowError Pair ParseError PartialQuickSort PermutedDimsArray Pipe PollingFileWatcher ProcessExitedException Ptr QuoteNode RandomDevice Range RangeIndex Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RevString RoundingMode RowVector SSAValue SegmentationFault SerializationState Set SharedArray SharedMatrix SharedVector Signed SimpleVector Slot SlotNumber SparseMatrixCSC SparseVector StackFrame StackOverflowError StackTrace StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubString SymTridiagonal Symbol Symmetric SystemError TCPSocket Task Text TextDisplay Timer Tridiagonal Tuple Type TypeError TypeMapEntry TypeMapLevel TypeName TypeVar TypedSlot UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefRefError UndefVarError UnicodeError UniformScaling Union UnionAll UnitRange Unsigned UpperTriangular Val Vararg VecElement VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef WorkerConfig WorkerPool "},a={keywords:t,illegal:/<\//},n={className:"subst",begin:/\$\(/,end:/\)/,keywords:t},o={className:"variable",begin:"\\$"+r},i={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],variants:[{begin:/\w*"""/,end:/"""\w*/,relevance:10},{begin:/\w*"/,end:/"\w*/}]},l={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],begin:"`",end:"`"},s={className:"meta",begin:"@"+r};return a.name="Julia",a.contains=[{className:"number",begin:/(\b0x[\d_]*(\.[\d_]*)?|0x\.\d[\d_]*)p[-+]?\d+|\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\b\d[\d_]*(\.[\d_]*)?|\.\d[\d_]*)([eEfF][-+]?\d+)?/,relevance:0},{className:"string",begin:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},i,l,s,{className:"comment",variants:[{begin:"#=",end:"=#",relevance:10},{begin:"#",end:"$"}]},e.HASH_COMMENT_MODE,{className:"keyword",begin:"\\b(((abstract|primitive)\\s+)type|(mutable\\s+)?struct)\\b"},{begin:/<:/}],n.contains=a.contains,a}}());hljs.registerLanguage("php-template",function(){"use strict";return function(n){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}());hljs.registerLanguage("scss",function(){"use strict";return function(e){var t={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},i={className:"number",begin:"#[0-9A-Fa-f]+"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:"\\#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{className:"selector-tag",begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",relevance:0},{className:"selector-pseudo",begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{className:"selector-pseudo",begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},t,{className:"attribute",begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",illegal:"[^\\s]"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:":",end:";",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,keywords:"and or not only",contains:[{begin:"@[a-z-]+",className:"keyword"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}());hljs.registerLanguage("r",function(){"use strict";return function(e){var n="([a-zA-Z]|\\.[a-zA-Z.])[a-zA-Z0-9._]*";return{name:"R",contains:[e.HASH_COMMENT_MODE,{begin:n,keywords:{$pattern:n,keyword:"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...",literal:"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10"},relevance:0},{className:"number",begin:"0[xX][0-9a-fA-F]+[Li]?\\b",relevance:0},{className:"number",begin:"\\d+(?:[eE][+\\-]?\\d*)?L\\b",relevance:0},{className:"number",begin:"\\d+\\.(?!\\d)(?:i\\b)?",relevance:0},{className:"number",begin:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{className:"number",begin:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{begin:"`",end:"`",relevance:0},{className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:'"',end:'"'},{begin:"'",end:"'"}]}]}}}());hljs.registerLanguage("sql",function(){"use strict";return function(e){var t=e.COMMENT("--","$");return{name:"SQL",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with",end:/;/,endsWithParent:!0,keywords:{$pattern:/[\w\.]+/,keyword:"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null unknown",built_in:"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void"},contains:[{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:'"',end:'"',contains:[{begin:'""'}]},{className:"string",begin:"`",end:"`"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}());hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}());hljs.registerLanguage("json",function(){"use strict";return function(n){var e={literal:"true false null"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:",",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(a,{begin:/:/})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(a)],illegal:"\\S"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:"JSON",contains:t,keywords:e,illegal:"\\S"}}}());hljs.registerLanguage("python-repl",function(){"use strict";return function(n){return{aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}}());hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}());hljs.registerLanguage("javascript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return r("(?=",e,")")}function r(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(t){var i="[A-Za-z$_][0-9A-Za-z$_]*",c={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/},o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.join(" "),literal:n.join(" "),built_in:a.join(" ")},l={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:t.C_NUMBER_RE+"n?"}],relevance:0},E={className:"subst",begin:"\\$\\{",end:"\\}",keywords:o,contains:[]},d={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},g={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},u={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]};E.contains=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,l,t.REGEXP_MODE];var b=E.contains.concat([{begin:/\(/,end:/\)/,contains:["self"].concat(E.contains,[t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE])},t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]),_={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:b};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:o,contains:[t.SHEBANG({binary:"node",relevance:5}),{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,t.C_LINE_COMMENT_MODE,t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0},{className:"variable",begin:i+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,l,{begin:r(/[{,\n]\s*/,s(r(/(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/,i+"\\s*:"))),relevance:0,contains:[{className:"attr",begin:i+s("\\s*:"),relevance:0}]},{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,t.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:b}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:c.begin,end:c.end}],subLanguage:"xml",contains:[{begin:c.begin,end:c.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:i}),_],illegal:/\[|%/},{begin:/\$[(.]/},t.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0},{begin:"(get|set)\\s+(?="+i+"\\()",end:/{/,keywords:"get set",contains:[t.inherit(t.TITLE_MODE,{begin:i}),{begin:/\(\)/},_]}],illegal:/#(?!!)/}}}());hljs.registerLanguage("typescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "),literal:n.join(" "),built_in:a.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ")},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},i={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:r.C_NUMBER_RE+"n?"}],relevance:0},o={className:"subst",begin:"\\$\\{",end:"\\}",keywords:t,contains:[]},c={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"xml"}},l={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"css"}},E={className:"string",begin:"`",end:"`",contains:[r.BACKSLASH_ESCAPE,o]};o.contains=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,i,r.REGEXP_MODE];var d={begin:"\\(",end:/\)/,keywords:t,contains:["self",r.QUOTE_STRING_MODE,r.APOS_STRING_MODE,r.NUMBER_MODE]},u={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,s,d]};return{name:"TypeScript",aliases:["ts"],keywords:t,contains:[r.SHEBANG(),{className:"meta",begin:/^\s*['"]use strict['"]/},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,i,{begin:"("+r.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,r.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+r.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:r.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:d.contains}]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[\{;]/,excludeEnd:!0,keywords:t,contains:["self",r.inherit(r.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),u],illegal:/%/,relevance:0},{beginKeywords:"constructor",end:/[\{;]/,excludeEnd:!0,contains:["self",u]},{begin:/module\./,keywords:{built_in:"module"},relevance:0},{beginKeywords:"module",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:"interface extends"},{begin:/\$[(.]/},{begin:"\\."+r.IDENT_RE,relevance:0},s,d]}}}());hljs.registerLanguage("plaintext",function(){"use strict";return function(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}}());hljs.registerLanguage("less",function(){"use strict";return function(e){var n="([\\w-]+|@{[\\w-]+})",a=[],s=[],t=function(e){return{className:"string",begin:"~?"+e+".*?"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:"\\(",end:"\\)",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@{[\\w-]+}"),r("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},{className:"meta",begin:"!important"});var c=s.concat({begin:"{",end:"}",contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},o={begin:n+"\\s*:",returnBegin:!0,end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},g={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},d={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:"{"}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@{[\\w-]+}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"\\(",end:"\\)",contains:c},{begin:"!important"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}}());hljs.registerLanguage("lua",function(){"use strict";return function(e){var t={begin:"\\[=*\\[",end:"\\]=*\\]",contains:["self"]},a=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[","\\]=*\\]",{contains:[t],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:a.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:"\\[=*\\[",end:"\\]=*\\]",contains:[t],relevance:5}])}}}()); diff --git a/rfc/index.html b/rfc/index.html new file mode 100644 index 000000000000..ff319b10a662 --- /dev/null +++ b/rfc/index.html @@ -0,0 +1,261 @@ + + + + + + Introduction - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Introduction

+

Kani is an open-source verification tool that uses automated reasoning to analyze Rust programs. In order to +integrate feedback from developers and users on future changes to Kani, we decided to follow a light-weight +"RFC" (request for comments) process.

+

When to create an RFC

+

You should create an RFC in one of two cases:

+
    +
  1. The change you are proposing would be a "one way door": e.g. a major change to the public API, a new feature that would be difficult to modify once released, etc.
  2. +
  3. The change you are making has a significant design component, and would benefit from a design review.
  4. +
+

Bugs and improvements to existing features do not require an RFC. +If you are in doubt, feel free to create a feature request and discuss the next steps in the new issue. +Your PR reviewer may also request an RFC if your change appears to fall into category 1 or 2.

+

You do not necessarily need to create an RFC immediately. It is our experience that it is often best to write some "proof of concept" code to test out possible ideas before writing the formal RFC.

+

The RFC process

+

This is the overall workflow for the RFC process:

+
    Create RFC ──> Receive Feedback  ──> Accepted?
+                        │  ∧                  │ Y
+                        ∨  │                  ├───> Implement ───> Test + Feedback ───> Stabilize?
+                       Revise                 │ N                                          │ Y
+                                              └───> Close PR                               ├───> RFC Stable
+                                                                                           │ N
+                                                                                           └───> Remove feature
+
+
    +
  1. Create an RFC +
      +
    1. Create a tracking issue for your RFC (e.g.: Issue-1588).
    2. +
    3. Start from a fork of the Kani repository.
    4. +
    5. Copy the template file (rfc/src/template.md) to rfc/src/rfcs/<ID_NUMBER><my-feature>.md.
    6. +
    7. Fill in the details according to the template instructions.
    8. +
    +
      +
    • For the first RFC version, we recommend that you leave the "Software Design" section empty.
    • +
    • Focus on the user impact and user experience. +Include a few usage examples if possible.
    • +
    +
      +
    1. Add a link to the new RFC inside rfc/src/SUMMARY.md
    2. +
    3. Submit a pull request.
    4. +
    +
  2. +
  3. Build consensus and integrate feedback. +
      +
    1. RFCs should get approved by at least 2 Kani developers.
    2. +
    3. Once the RFC has been approved, update the RFC status and merge the PR.
    4. +
    5. If the RFC is not approved, close the PR without merging.
    6. +
    +
  4. +
  5. Feature implementation. +
      +
    1. Start implementing the new feature in your fork.
    2. +
    3. Create a new revision of the RFC to add details about the "Software Design".
    4. +
    5. It is OK to implement the feature incrementally over multiple PRs. +Just ensure that every pull request has a testable end-to-end flow and that it is properly tested.
    6. +
    7. In the implementation stage, the feature should only be accessible if the user explicitly passes +-Z <FEATURE_ID> as an argument to Kani.
    8. +
    9. Document how to use the feature.
    10. +
    11. Keep the RFC up-to-date with the decisions you make during implementation.
    12. +
    +
  6. +
  7. Test and Gather Feedback. +
      +
    1. Fix major issues related to the new feature.
    2. +
    3. Gather user feedback and make necessary adjustments.
    4. +
    5. Resolve RFC open questions.
    6. +
    7. Add regression tests to cover all expected behaviors and unit tests whenever possible.
    8. +
    +
  8. +
  9. Stabilization. +
      +
    1. Propose to stabilize the feature when feature is well tested and UX has received positive feedback.
    2. +
    3. Create a new PR that removes the -Z <FEATURE_ID> guard and that marks the RFC status as "STABLE". +
        +
      1. Make sure the RFC reflects the final implementation and user experience.
      2. +
      +
    4. +
    5. In some cases, we might decide not to incorporate a feature +(E.g.: performance degradation, bad user experience, better alternative). +In those cases, please update the RFC status to "CANCELLED as per <PR_LINK | ISSUE_LINK>" and remove the code +that is no longer relevant.
    6. +
    7. Close the tracking issue.
    8. +
    +
  10. +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/intro.html b/rfc/intro.html new file mode 100644 index 000000000000..b150542743c1 --- /dev/null +++ b/rfc/intro.html @@ -0,0 +1,261 @@ + + + + + + Introduction - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Introduction

+

Kani is an open-source verification tool that uses automated reasoning to analyze Rust programs. In order to +integrate feedback from developers and users on future changes to Kani, we decided to follow a light-weight +"RFC" (request for comments) process.

+

When to create an RFC

+

You should create an RFC in one of two cases:

+
    +
  1. The change you are proposing would be a "one way door": e.g. a major change to the public API, a new feature that would be difficult to modify once released, etc.
  2. +
  3. The change you are making has a significant design component, and would benefit from a design review.
  4. +
+

Bugs and improvements to existing features do not require an RFC. +If you are in doubt, feel free to create a feature request and discuss the next steps in the new issue. +Your PR reviewer may also request an RFC if your change appears to fall into category 1 or 2.

+

You do not necessarily need to create an RFC immediately. It is our experience that it is often best to write some "proof of concept" code to test out possible ideas before writing the formal RFC.

+

The RFC process

+

This is the overall workflow for the RFC process:

+
    Create RFC ──> Receive Feedback  ──> Accepted?
+                        │  ∧                  │ Y
+                        ∨  │                  ├───> Implement ───> Test + Feedback ───> Stabilize?
+                       Revise                 │ N                                          │ Y
+                                              └───> Close PR                               ├───> RFC Stable
+                                                                                           │ N
+                                                                                           └───> Remove feature
+
+
    +
  1. Create an RFC +
      +
    1. Create a tracking issue for your RFC (e.g.: Issue-1588).
    2. +
    3. Start from a fork of the Kani repository.
    4. +
    5. Copy the template file (rfc/src/template.md) to rfc/src/rfcs/<ID_NUMBER><my-feature>.md.
    6. +
    7. Fill in the details according to the template instructions.
    8. +
    +
      +
    • For the first RFC version, we recommend that you leave the "Software Design" section empty.
    • +
    • Focus on the user impact and user experience. +Include a few usage examples if possible.
    • +
    +
      +
    1. Add a link to the new RFC inside rfc/src/SUMMARY.md
    2. +
    3. Submit a pull request.
    4. +
    +
  2. +
  3. Build consensus and integrate feedback. +
      +
    1. RFCs should get approved by at least 2 Kani developers.
    2. +
    3. Once the RFC has been approved, update the RFC status and merge the PR.
    4. +
    5. If the RFC is not approved, close the PR without merging.
    6. +
    +
  4. +
  5. Feature implementation. +
      +
    1. Start implementing the new feature in your fork.
    2. +
    3. Create a new revision of the RFC to add details about the "Software Design".
    4. +
    5. It is OK to implement the feature incrementally over multiple PRs. +Just ensure that every pull request has a testable end-to-end flow and that it is properly tested.
    6. +
    7. In the implementation stage, the feature should only be accessible if the user explicitly passes +-Z <FEATURE_ID> as an argument to Kani.
    8. +
    9. Document how to use the feature.
    10. +
    11. Keep the RFC up-to-date with the decisions you make during implementation.
    12. +
    +
  6. +
  7. Test and Gather Feedback. +
      +
    1. Fix major issues related to the new feature.
    2. +
    3. Gather user feedback and make necessary adjustments.
    4. +
    5. Resolve RFC open questions.
    6. +
    7. Add regression tests to cover all expected behaviors and unit tests whenever possible.
    8. +
    +
  8. +
  9. Stabilization. +
      +
    1. Propose to stabilize the feature when feature is well tested and UX has received positive feedback.
    2. +
    3. Create a new PR that removes the -Z <FEATURE_ID> guard and that marks the RFC status as "STABLE". +
        +
      1. Make sure the RFC reflects the final implementation and user experience.
      2. +
      +
    4. +
    5. In some cases, we might decide not to incorporate a feature +(E.g.: performance degradation, bad user experience, better alternative). +In those cases, please update the RFC status to "CANCELLED as per <PR_LINK | ISSUE_LINK>" and remove the code +that is no longer relevant.
    6. +
    7. Close the tracking issue.
    8. +
    +
  10. +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/mark.min.js b/rfc/mark.min.js new file mode 100644 index 000000000000..163623188347 --- /dev/null +++ b/rfc/mark.min.js @@ -0,0 +1,7 @@ +/*!*************************************************** +* mark.js v8.11.1 +* https://markjs.io/ +* Copyright (c) 2014–2018, Julian Kühnel +* Released under the MIT license https://git.io/vwTVl +*****************************************************/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Mark=t()}(this,function(){"use strict";var e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:5e3;t(this,e),this.ctx=n,this.iframes=r,this.exclude=i,this.iframesTimeout=o}return n(e,[{key:"getContexts",value:function(){var e=[];return(void 0!==this.ctx&&this.ctx?NodeList.prototype.isPrototypeOf(this.ctx)?Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?this.ctx:"string"==typeof this.ctx?Array.prototype.slice.call(document.querySelectorAll(this.ctx)):[this.ctx]:[]).forEach(function(t){var n=e.filter(function(e){return e.contains(t)}).length>0;-1!==e.indexOf(t)||n||e.push(t)}),e}},{key:"getIframeContents",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){},r=void 0;try{var i=e.contentWindow;if(r=i.document,!i||!r)throw new Error("iframe inaccessible")}catch(e){n()}r&&t(r)}},{key:"isIframeBlank",value:function(e){var t="about:blank",n=e.getAttribute("src").trim();return e.contentWindow.location.href===t&&n!==t&&n}},{key:"observeIframeLoad",value:function(e,t,n){var r=this,i=!1,o=null,a=function a(){if(!i){i=!0,clearTimeout(o);try{r.isIframeBlank(e)||(e.removeEventListener("load",a),r.getIframeContents(e,t,n))}catch(e){n()}}};e.addEventListener("load",a),o=setTimeout(a,this.iframesTimeout)}},{key:"onIframeReady",value:function(e,t,n){try{"complete"===e.contentWindow.document.readyState?this.isIframeBlank(e)?this.observeIframeLoad(e,t,n):this.getIframeContents(e,t,n):this.observeIframeLoad(e,t,n)}catch(e){n()}}},{key:"waitForIframes",value:function(e,t){var n=this,r=0;this.forEachIframe(e,function(){return!0},function(e){r++,n.waitForIframes(e.querySelector("html"),function(){--r||t()})},function(e){e||t()})}},{key:"forEachIframe",value:function(t,n,r){var i=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},a=t.querySelectorAll("iframe"),s=a.length,c=0;a=Array.prototype.slice.call(a);var u=function(){--s<=0&&o(c)};s||u(),a.forEach(function(t){e.matches(t,i.exclude)?u():i.onIframeReady(t,function(e){n(t)&&(c++,r(e)),u()},u)})}},{key:"createIterator",value:function(e,t,n){return document.createNodeIterator(e,t,n,!1)}},{key:"createInstanceOnIframe",value:function(t){return new e(t.querySelector("html"),this.iframes)}},{key:"compareNodeIframe",value:function(e,t,n){if(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_PRECEDING){if(null===t)return!0;if(t.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)return!0}return!1}},{key:"getIteratorNode",value:function(e){var t=e.previousNode();return{prevNode:t,node:null===t?e.nextNode():e.nextNode()&&e.nextNode()}}},{key:"checkIframeFilter",value:function(e,t,n,r){var i=!1,o=!1;return r.forEach(function(e,t){e.val===n&&(i=t,o=e.handled)}),this.compareNodeIframe(e,t,n)?(!1!==i||o?!1===i||o||(r[i].handled=!0):r.push({val:n,handled:!0}),!0):(!1===i&&r.push({val:n,handled:!1}),!1)}},{key:"handleOpenIframes",value:function(e,t,n,r){var i=this;e.forEach(function(e){e.handled||i.getIframeContents(e.val,function(e){i.createInstanceOnIframe(e).forEachNode(t,n,r)})})}},{key:"iterateThroughNodes",value:function(e,t,n,r,i){for(var o,a=this,s=this.createIterator(t,e,r),c=[],u=[],l=void 0,h=void 0;void 0,o=a.getIteratorNode(s),h=o.prevNode,l=o.node;)this.iframes&&this.forEachIframe(t,function(e){return a.checkIframeFilter(l,h,e,c)},function(t){a.createInstanceOnIframe(t).forEachNode(e,function(e){return u.push(e)},r)}),u.push(l);u.forEach(function(e){n(e)}),this.iframes&&this.handleOpenIframes(c,e,n,r),i()}},{key:"forEachNode",value:function(e,t,n){var r=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},o=this.getContexts(),a=o.length;a||i(),o.forEach(function(o){var s=function(){r.iterateThroughNodes(e,o,t,n,function(){--a<=0&&i()})};r.iframes?r.waitForIframes(o,s):s()})}}],[{key:"matches",value:function(e,t){var n="string"==typeof t?[t]:t,r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector;if(r){var i=!1;return n.every(function(t){return!r.call(e,t)||(i=!0,!1)}),i}return!1}}]),e}(),o=function(){function e(n){t(this,e),this.opt=r({},{diacritics:!0,synonyms:{},accuracy:"partially",caseSensitive:!1,ignoreJoiners:!1,ignorePunctuation:[],wildcards:"disabled"},n)}return n(e,[{key:"create",value:function(e){return"disabled"!==this.opt.wildcards&&(e=this.setupWildcardsRegExp(e)),e=this.escapeStr(e),Object.keys(this.opt.synonyms).length&&(e=this.createSynonymsRegExp(e)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),this.opt.diacritics&&(e=this.createDiacriticsRegExp(e)),e=this.createMergedBlanksRegExp(e),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.createJoinersRegExp(e)),"disabled"!==this.opt.wildcards&&(e=this.createWildcardsRegExp(e)),e=this.createAccuracyRegExp(e),new RegExp(e,"gm"+(this.opt.caseSensitive?"":"i"))}},{key:"escapeStr",value:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}},{key:"createSynonymsRegExp",value:function(e){var t=this.opt.synonyms,n=this.opt.caseSensitive?"":"i",r=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(var i in t)if(t.hasOwnProperty(i)){var o=t[i],a="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(i):this.escapeStr(i),s="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(o):this.escapeStr(o);""!==a&&""!==s&&(e=e.replace(new RegExp("("+this.escapeStr(a)+"|"+this.escapeStr(s)+")","gm"+n),r+"("+this.processSynonyms(a)+"|"+this.processSynonyms(s)+")"+r))}return e}},{key:"processSynonyms",value:function(e){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),e}},{key:"setupWildcardsRegExp",value:function(e){return(e=e.replace(/(?:\\)*\?/g,function(e){return"\\"===e.charAt(0)?"?":""})).replace(/(?:\\)*\*/g,function(e){return"\\"===e.charAt(0)?"*":""})}},{key:"createWildcardsRegExp",value:function(e){var t="withSpaces"===this.opt.wildcards;return e.replace(/\u0001/g,t?"[\\S\\s]?":"\\S?").replace(/\u0002/g,t?"[\\S\\s]*?":"\\S*")}},{key:"setupIgnoreJoinersRegExp",value:function(e){return e.replace(/[^(|)\\]/g,function(e,t,n){var r=n.charAt(t+1);return/[(|)\\]/.test(r)||""===r?e:e+"\0"})}},{key:"createJoinersRegExp",value:function(e){var t=[],n=this.opt.ignorePunctuation;return Array.isArray(n)&&n.length&&t.push(this.escapeStr(n.join(""))),this.opt.ignoreJoiners&&t.push("\\u00ad\\u200b\\u200c\\u200d"),t.length?e.split(/\u0000+/).join("["+t.join("")+"]*"):e}},{key:"createDiacriticsRegExp",value:function(e){var t=this.opt.caseSensitive?"":"i",n=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"],r=[];return e.split("").forEach(function(i){n.every(function(n){if(-1!==n.indexOf(i)){if(r.indexOf(n)>-1)return!1;e=e.replace(new RegExp("["+n+"]","gm"+t),"["+n+"]"),r.push(n)}return!0})}),e}},{key:"createMergedBlanksRegExp",value:function(e){return e.replace(/[\s]+/gim,"[\\s]+")}},{key:"createAccuracyRegExp",value:function(e){var t=this,n=this.opt.accuracy,r="string"==typeof n?n:n.value,i="";switch(("string"==typeof n?[]:n.limiters).forEach(function(e){i+="|"+t.escapeStr(e)}),r){case"partially":default:return"()("+e+")";case"complementary":return"()([^"+(i="\\s"+(i||this.escapeStr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿")))+"]*"+e+"[^"+i+"]*)";case"exactly":return"(^|\\s"+i+")("+e+")(?=$|\\s"+i+")"}}}]),e}(),a=function(){function a(e){t(this,a),this.ctx=e,this.ie=!1;var n=window.navigator.userAgent;(n.indexOf("MSIE")>-1||n.indexOf("Trident")>-1)&&(this.ie=!0)}return n(a,[{key:"log",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"debug",r=this.opt.log;this.opt.debug&&"object"===(void 0===r?"undefined":e(r))&&"function"==typeof r[n]&&r[n]("mark.js: "+t)}},{key:"getSeparatedKeywords",value:function(e){var t=this,n=[];return e.forEach(function(e){t.opt.separateWordSearch?e.split(" ").forEach(function(e){e.trim()&&-1===n.indexOf(e)&&n.push(e)}):e.trim()&&-1===n.indexOf(e)&&n.push(e)}),{keywords:n.sort(function(e,t){return t.length-e.length}),length:n.length}}},{key:"isNumeric",value:function(e){return Number(parseFloat(e))==e}},{key:"checkRanges",value:function(e){var t=this;if(!Array.isArray(e)||"[object Object]"!==Object.prototype.toString.call(e[0]))return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(e),[];var n=[],r=0;return e.sort(function(e,t){return e.start-t.start}).forEach(function(e){var i=t.callNoMatchOnInvalidRanges(e,r),o=i.start,a=i.end;i.valid&&(e.start=o,e.length=a-o,n.push(e),r=a)}),n}},{key:"callNoMatchOnInvalidRanges",value:function(e,t){var n=void 0,r=void 0,i=!1;return e&&void 0!==e.start?(r=(n=parseInt(e.start,10))+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&r-t>0&&r-n>0?i=!0:(this.log("Ignoring invalid or overlapping range: "+JSON.stringify(e)),this.opt.noMatch(e))):(this.log("Ignoring invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:n,end:r,valid:i}}},{key:"checkWhitespaceRanges",value:function(e,t,n){var r=void 0,i=!0,o=n.length,a=t-o,s=parseInt(e.start,10)-a;return(r=(s=s>o?o:s)+parseInt(e.length,10))>o&&(r=o,this.log("End range automatically set to the max value of "+o)),s<0||r-s<0||s>o||r>o?(i=!1,this.log("Invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)):""===n.substring(s,r).replace(/\s+/g,"")&&(i=!1,this.log("Skipping whitespace only range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:s,end:r,valid:i}}},{key:"getTextNodes",value:function(e){var t=this,n="",r=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,function(e){r.push({start:n.length,end:(n+=e.textContent).length,node:e})},function(e){return t.matchesExclude(e.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},function(){e({value:n,nodes:r})})}},{key:"matchesExclude",value:function(e){return i.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}},{key:"wrapRangeInTextNode",value:function(e,t,n){var r=this.opt.element?this.opt.element:"mark",i=e.splitText(t),o=i.splitText(n-t),a=document.createElement(r);return a.setAttribute("data-markjs","true"),this.opt.className&&a.setAttribute("class",this.opt.className),a.textContent=i.textContent,i.parentNode.replaceChild(a,i),o}},{key:"wrapRangeInMappedTextNode",value:function(e,t,n,r,i){var o=this;e.nodes.every(function(a,s){var c=e.nodes[s+1];if(void 0===c||c.start>t){if(!r(a.node))return!1;var u=t-a.start,l=(n>a.end?a.end:n)-a.start,h=e.value.substr(0,a.start),f=e.value.substr(l+a.start);if(a.node=o.wrapRangeInTextNode(a.node,u,l),e.value=h+f,e.nodes.forEach(function(t,n){n>=s&&(e.nodes[n].start>0&&n!==s&&(e.nodes[n].start-=l),e.nodes[n].end-=l)}),n-=l,i(a.node.previousSibling,a.start),!(n>a.end))return!1;t=a.end}return!0})}},{key:"wrapGroups",value:function(e,t,n,r){return r((e=this.wrapRangeInTextNode(e,t,t+n)).previousSibling),e}},{key:"separateGroups",value:function(e,t,n,r,i){for(var o=t.length,a=1;a-1&&r(t[a],e)&&(e=this.wrapGroups(e,s,t[a].length,i))}return e}},{key:"wrapMatches",value:function(e,t,n,r,i){var o=this,a=0===t?0:t+1;this.getTextNodes(function(t){t.nodes.forEach(function(t){t=t.node;for(var i=void 0;null!==(i=e.exec(t.textContent))&&""!==i[a];){if(o.opt.separateGroups)t=o.separateGroups(t,i,a,n,r);else{if(!n(i[a],t))continue;var s=i.index;if(0!==a)for(var c=1;c + + + + + Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Introduction

+

Kani is an open-source verification tool that uses automated reasoning to analyze Rust programs. In order to +integrate feedback from developers and users on future changes to Kani, we decided to follow a light-weight +"RFC" (request for comments) process.

+

When to create an RFC

+

You should create an RFC in one of two cases:

+
    +
  1. The change you are proposing would be a "one way door": e.g. a major change to the public API, a new feature that would be difficult to modify once released, etc.
  2. +
  3. The change you are making has a significant design component, and would benefit from a design review.
  4. +
+

Bugs and improvements to existing features do not require an RFC. +If you are in doubt, feel free to create a feature request and discuss the next steps in the new issue. +Your PR reviewer may also request an RFC if your change appears to fall into category 1 or 2.

+

You do not necessarily need to create an RFC immediately. It is our experience that it is often best to write some "proof of concept" code to test out possible ideas before writing the formal RFC.

+

The RFC process

+

This is the overall workflow for the RFC process:

+
    Create RFC ──> Receive Feedback  ──> Accepted?
+                        │  ∧                  │ Y
+                        ∨  │                  ├───> Implement ───> Test + Feedback ───> Stabilize?
+                       Revise                 │ N                                          │ Y
+                                              └───> Close PR                               ├───> RFC Stable
+                                                                                           │ N
+                                                                                           └───> Remove feature
+
+
    +
  1. Create an RFC +
      +
    1. Create a tracking issue for your RFC (e.g.: Issue-1588).
    2. +
    3. Start from a fork of the Kani repository.
    4. +
    5. Copy the template file (rfc/src/template.md) to rfc/src/rfcs/<ID_NUMBER><my-feature>.md.
    6. +
    7. Fill in the details according to the template instructions.
    8. +
    +
      +
    • For the first RFC version, we recommend that you leave the "Software Design" section empty.
    • +
    • Focus on the user impact and user experience. +Include a few usage examples if possible.
    • +
    +
      +
    1. Add a link to the new RFC inside rfc/src/SUMMARY.md
    2. +
    3. Submit a pull request.
    4. +
    +
  2. +
  3. Build consensus and integrate feedback. +
      +
    1. RFCs should get approved by at least 2 Kani developers.
    2. +
    3. Once the RFC has been approved, update the RFC status and merge the PR.
    4. +
    5. If the RFC is not approved, close the PR without merging.
    6. +
    +
  4. +
  5. Feature implementation. +
      +
    1. Start implementing the new feature in your fork.
    2. +
    3. Create a new revision of the RFC to add details about the "Software Design".
    4. +
    5. It is OK to implement the feature incrementally over multiple PRs. +Just ensure that every pull request has a testable end-to-end flow and that it is properly tested.
    6. +
    7. In the implementation stage, the feature should only be accessible if the user explicitly passes +-Z <FEATURE_ID> as an argument to Kani.
    8. +
    9. Document how to use the feature.
    10. +
    11. Keep the RFC up-to-date with the decisions you make during implementation.
    12. +
    +
  6. +
  7. Test and Gather Feedback. +
      +
    1. Fix major issues related to the new feature.
    2. +
    3. Gather user feedback and make necessary adjustments.
    4. +
    5. Resolve RFC open questions.
    6. +
    7. Add regression tests to cover all expected behaviors and unit tests whenever possible.
    8. +
    +
  8. +
  9. Stabilization. +
      +
    1. Propose to stabilize the feature when feature is well tested and UX has received positive feedback.
    2. +
    3. Create a new PR that removes the -Z <FEATURE_ID> guard and that marks the RFC status as "STABLE". +
        +
      1. Make sure the RFC reflects the final implementation and user experience.
      2. +
      +
    4. +
    5. In some cases, we might decide not to incorporate a feature +(E.g.: performance degradation, bad user experience, better alternative). +In those cases, please update the RFC status to "CANCELLED as per <PR_LINK | ISSUE_LINK>" and remove the code +that is no longer relevant.
    6. +
    7. Close the tracking issue.
    8. +
    +
  10. +
+
    +
  • Feature Name: Fill me with pretty name and a unique ident 1. Example: New Feature (new_feature)
  • +
  • Feature Request Issue: Link to issue
  • +
  • RFC PR: Link to original PR
  • +
  • Status: One of the following: [Under Review | Unstable | Stable | Cancelled]
  • +
  • Version: [0-9]* Increment this version whenever you open a new PR to update the RFC (not at every revision). +Start with 0.
  • +
  • Proof-of-concept: Optional field. If you have implemented a proof of concept, add a link here
  • +
+
+

Summary

+

Short (1-2 sentences) description of the feature. What is this feature about?

+

User Impact

+

Imagine this as your elevator pitch directed to users as well as Kani developers.

+
    +
  • Why are we doing this?
  • +
  • Why should users care about this feature?
  • +
  • How will this benefit them?
  • +
  • What is the downside?
  • +
+

If this RFC is related to change in the architecture without major user impact, +think about the long term impact for user. +I.e.: what future work will this enable.

+
    +
  • If you are unsure you need an RFC, please create a feature request issue and discuss the need with other Kani developers.
  • +
+

User Experience

+

This should be a description on how users will interact with the feature. +Users should be able to read this section and understand how to use the feature. +Do not include implementation details in this section, neither discuss the rationale behind the chosen UX.

+

Please include:

+
    +
  • High level user flow description.
  • +
  • Any new major functions or attributes that will be added to Kani library.
  • +
  • New command line options or subcommands (no need to mention the unstable flag).
  • +
  • List failure scenarios and how are they presented (e.g., compilation errors, verification failures, and possible failed user iterations).
  • +
  • Substantial changes to existing functionality or Kani output.
  • +
+

If the RFC is related to architectural changes and there are no visible changes to UX, please state so. +No further explanation is needed.

+

Software Design

+

We recommend that you leave the Software Design section empty for the first version of your RFC.

+

This is the beginning of the technical portion of the RFC. +From now on, your main audience is Kani developers, so it's OK to assume readers know Kani architecture.

+

Please provide a high level description your design.

+
    +
  • What are the main components that will be modified? (E.g.: changes to kani-compiler, kani-driver, metadata, proc-macros, installation...)
  • +
  • Will there be changes to the components interface?
  • +
  • Any changes to how these components communicate?
  • +
  • What corner cases do you anticipate?
  • +
+

Rationale and alternatives

+

This is the section where you discuss the decisions you made.

+
    +
  • What are the pros and cons of the UX? What would be the alternatives?
  • +
  • What is the impact of not doing this?
  • +
  • Any pros / cons on how you designed this?
  • +
+

Open questions

+

List of open questions + an optional link to an issue that captures the work required to address the open question. +Capture the details of each open question in their respective issue, not here.

+

Example:

+
    +
  • Is there any use case that isn't handled yet?
  • +
  • Is there any part of the UX that still needs some improvement?
  • +
+

Make sure all open questions are addressed before stabilization.

+

Out of scope / Future Improvements

+

Optional Section: List of extensions and possible improvements that you predict for this feature that is out of +the scope of this RFC.

+

Feel free to add as many items as you want, but please refrain from adding too much detail. +If you want to capture your thoughts or start a discussion, please create a feature request. +You are welcome to add a link to the new issue here.

+
1 +

This unique ident should be used to enable features proposed in the RFC using -Z <ident> until the feature has been stabilized.

+
+
+
+

Summary

+

Fix linking issues with the rust standard library in a scalable manner by only generating goto-program for +code that is reachable from the user harnesses.

+

User Impact

+

The main goal of this RFC is to enable Kani users to link against all supported constructs from the std library. +Currently, Kani will only link to items that are either generic or have an inline annotation.

+

The approach introduced in this RFC will have the following secondary benefits.

+
    +
  • Reduce spurious warnings about unsupported features for cases where the feature is not reachable from any harness.
  • +
  • In the verification mode, we will likely see a reduction on the compilation time and memory consumption +by pruning the inputs of symtab2gb and goto-instrument. +
      +
    • Compared to linking against the standard library goto-models that take more than 5 GB.
    • +
    +
  • +
  • In a potential assessment mode, only analyze code that is reachable from all public items in the target crate.
  • +
+

One downside is that we will include a pre-compiled version of the std, our release bundle will double in size +(See Rational and Alternatives +for more information on the size overhead). +This will negatively impact the time taken to set up Kani +(triggered by either the first time a user invokes kani | cargo-kani , or explicit invoke the subcommand setup).

+

User Experience

+

Once this RFC has been stabilized users shall use Kani in the same manner as they have been today. +Until then, we wil add an unstable option --mir-linker to enable the cross-crate reachability analysis +and the generation of the goto-program only when compiling the target crate.

+

Kani setup will likely take longer and more disk space as mentioned in the section above. +This change will not be guarded by --mir-linker option above.

+

Detailed Design

+

In a nutshell, we will no longer generate a goto-program for every crate we compile. +Instead, we will generate the MIR for every crate, and we will generate only one goto-program. +This model will only include items reachable from the target crate's harnesses.

+

The current system flow for a crate verification is the following (Kani here represents either kani | cargo-kani +executable):

+
    +
  1. Kani compiles the user crate as well as all its dependencies. +For every crate compiled, kani-compiler will generate a goto-program. +This model includes everything reachable from the crate's public functions.
  2. +
  3. After that, Kani links all models together by invoking goto-cc. +This step will also link against Kani's C library.
  4. +
  5. For every harness, Kani invokes goto-instrument to prune the linked model to only include items reachable from the given harness.
  6. +
  7. Finally, Kani instruments and verify each harness model via goto-instrument and cbmc calls.
  8. +
+

After this RFC, the system flow would be slightly different:

+
    +
  1. Kani compiles the user crate dependencies up to the MIR translation. +I.e., for every crate compiled, kani-compiler will generate an artifact that includes the MIR representation +of all items in the crate.
  2. +
  3. Kani will generate the goto-program only while compiling the target user crate. +It will generate one goto-program that includes all items reachable from any harness in the target crate.
  4. +
  5. goto-cc will still be invoked to link the generated model against Kani's C library.
  6. +
  7. Steps #3 and #4 above will be performed without any change.
  8. +
+

This feature will require three main changes to Kani which are detailed in the sub-sections below.

+

Kani's Sysroot

+

Kani currently uses rustup sysroot to gather information from the standard library constructs. +The artifacts from this sysroot include the MIR for generic items as well as for items that may be included in +a crate compilation (e.g.: functions marked with #[inline] annotation). +The artifacts do not include the MIR for items that have already been compiled to the std shared library. +This leaves a gap that cannot be filled by the kani-compiler; +thus, we are unable to translate these items into goto-program.

+

In order to fulfill this gap, we must compile the standard library from scratch. +This RFC proposes a similar method to what MIRI implements. +We will generate our own sysroot using the -Z always-encode-mir compilation flag. +This sysroot will be pre-compiled and included in our release bundle.

+

We will compile kani's libraries (kani and std) also with -Z always-encode-mir +and with the new sysroot.

+

Cross-Crate Reachability Analysis

+

kani-compiler will include a new reachability module to traverse over the local and external MIR items. +This module will monomorphize all generic code as it's performing the traversal.

+

The traversal logic will be customizable allowing different starting points to be used. +The two options to be included in this RFC is starting from all local harnesses +(tagged with #[kani::proof]) and all public functions in the local crate.

+

The kani-compiler behavior will be customizable via a new flag:

+
--reachability=[ harnesses | pub_fns |  none | legacy | tests ]
+
+

where:

+
    +
  • harnesses: Use the local harnesses as the starting points for the reachability analysis.
  • +
  • pub_fns: Use the public local functions as the starting points for the reachability analysis.
  • +
  • none: This will be the default value if --reachability flag is not provided. It will skip +reachability analysis. No goto-program will be generated. +This will be used to compile dependencies up to the MIR level. +kani-compiler will still generate artifacts with the crate's MIR.
  • +
  • tests: Use the functions marked as tests with #[tests] as the starting points for the analysis.
  • +
  • legacy: Mimics rustc behavior by invoking +rustc_monomorphizer::collect_and_partition_mono_items() to collect the items to be generated. +This will not include many items that go beyond the crate boundary. +This option was only kept for now for internal usage in some of our compiler tests. +It cannot be used as part of the end to end verification flow, and it will be removed in the future.
  • +
+

These flags will not be exposed to the final user. +They will only be used for the communication between kani-driver and kani-compiler.

+

Dependencies vs Target Crate Compilation

+

The flags described in the section above will be used by kani-driver to implement the new system flow. +For that, we propose the following mechanism:

+
    +
  • +

    For standalone kani, we will pass the option --reachability=harnesses to kani-compiler.

    +
  • +
  • +

    For cargo-kani, we will replace

    +
    cargo build <FLAGS>
    +
    +

    with:

    +
    cargo rustc <FLAGS> -- --reachability=harnesses
    +
    +

    to build everything. +This command will compile all dependencies without the --reachability argument, and it will only pass harnesses +value to the compiler when compiling the target crate.

    +
  • +
+

Rational and Alternatives

+

Not doing anything is not an alternative, since this fixes a major gap in Kani's usability.

+

Benefits

+
    +
  • The MIR linker will allow us to fix the linking issues with Rust's standard library.
  • +
  • Once stabilized, the MIR linker will be transparent to the user.
  • +
  • It will enable more powerful and precise static analysis to kani-compiler.
  • +
  • It won't require any changes to our dependencies.
  • +
  • This will fix the harnesses' dependency on the#[no_mangle] annotation +(Issue-661).
  • +
+

Risks

+

Failures in the linking stage would not impact the tool soundness. I anticipate the following failure scenarios:

+
    +
  • ICE (Internal compiler error): Some logic is incorrectly implemented and the linking stage crashes. +Although this is a bad experience for the user, this will not impact the verification result.
  • +
  • Missing items: This would either result in ICE during code generation or a verification failure if the missing +item is reachable.
  • +
  • Extra items: This shouldn't impact the verification results, and they should be pruned by CBMC's reachability +analysis. +This is already the case today. In extreme cases, this could include a symbol that we cannot compile and cause an ICE.
  • +
+

The new reachability code would be highly dependent on the rustc unstable APIs, which could increase +the cost of the upstream synchronization. +That said, the APIs that would be required are already used today.

+

Finally, this implementation relies on a few unstable options from cargo and rustc. +These APIs are used by other tools such as MIRI, so we don't see a high risk that they would be removed.

+

Alternatives

+

The other options explored were:

+
    +
  1. Pre-compile the standard library, and the kani library, and ship the generated *symtab.json files.
  2. +
  3. Pre-compile the standard library, and the kani library, convert the standard library and dependencies to goto-program +(viasymtab2gb) and link them into one single goto-program. +Ship the generated model.
  4. +
+

Both would still require shipping the compiler metadata (via rlib or rmeta) for the kani library, its +dependencies, and kani_macro.so.

+

Both alternatives are very similar. They only differ on the artifact that would be shipped. +They require generating and shipping a custom sysroot; +however, there is no need to implement the reachability algorithm.

+

We implemented a prototype for the MIR linker and one for the alternatives. +Both prototypes generate the sysroot as part of the cargo kani flow.

+

We performed a small experiment (on a c5.4xlarge ec2 instance running Ubuntu 20.04) to assess the options.

+

For this experiment, we used the following harness:

+
#[kani::proof]
+#[kani::unwind(4)]
+pub fn check_format() {
+    assert!("2".parse::<u32>().unwrap() == 2);
+}
+
+

The experiment showed that the MIR linker approach is much more efficient.

+

See the table bellow for the breakdown of time (in seconds) taken for each major step of +the harness verification:

+ + + + + + +
StageMIR LinkerAlternative 1
compilation22.2s64.7s
goto-program generation2.4s90.7s
goto-program linking0.8s33.2s
code instrumentation0.8s33.1
verification0.5s8.5s
+

It is possible that goto-cc time can be improved, but this would also require further experimentation and +expertise that we don't have today.

+

Every option would require a custom sysroot to either be built or shipped with Kani. +The table below shows the size of the sysroot files for the alternative #2 +(goto-program files) vs compiler artifacts (*.rmeta files) +files with -Z always-encode-mir for x86_64-unknown-linux-gnu (on Ubuntu 18.04).

+ + + + +
File TypeRaw sizeCompressed size
symtab.json950M26M
symtab.out84M24M
*.rmeta92M25M
+

These results were obtained by looking at the artifacts generated during the same experiment.

+

Open questions

+
    +
  • Should we build or download the sysroot during kani setup? +We include pre-built MIR artifacts for the std library.
  • +
  • What's the best way to enable support to run Kani in the entire workspace? +We decided to run cargo rustc per package.
  • +
  • Should we codegen all static items no matter what? +We only generate code for static items that are collected by the reachability analysis. +Static objects can only be initialized via constant function. +Thus, it shouldn't have any side effect.
  • +
  • What's the best way to handle cargo kani --tests? +We are going to use the test profile and iterate over all the targets available in the crate: +
      +
    • cargo rustc --profile test -- --reachability=harnesses
    • +
    +
  • +
+

Future possibilities

+
    +
  • Split the goto-program into two or more items to optimize compilation result caching. +
      +
    • Dependencies: One model will include items from all the crate dependencies. +This model will likely be more stable and require fewer updates.
    • +
    • Target crate: The model for all items in the target crate.
    • +
    +
  • +
  • Do the analysis per-harness. This might be adequate once we have a mechanism to cache translations.
  • +
  • Add an option to include external functions to the analysis starting point in order to enable verification when +calls are made from C to rust.
  • +
  • Contribute the reachability analysis code back to upstream.
  • +
+
+
+

Summary

+

Allow users to specify that certain functions and methods should be replaced with mock functions (stubs) during verification.

+

Scope

+

In scope:

+
    +
  • Replacing function bodies
  • +
  • Replacing method bodies (which means that the new method body will be executed, whether the method is invoked directly or through a vtable)
  • +
+

Out of scope:

+
    +
  • Replacing type definitions
  • +
  • Replacing macro definitions
  • +
  • Mocking traits
  • +
  • Mocking intrinsics
  • +
+

User impact

+

We anticipate that function/method stubbing will have a substantial positive impact on the usability of Kani:

+
    +
  1. Users might need to stub functions/methods containing features that Kani does not support, such as inline assembly.
  2. +
  3. Users might need to stub functions/methods containing code that Kani supports in principle, but which in practice leads to bad verification performance (for example, if it contains deserialization code).
  4. +
  5. Users could use stubbing to perform compositional reasoning: prove the behavior of a function/method f, and then in other proofs---that call f indirectly---use a stub of f that mocks that behavior but is less complex.
  6. +
+

In all cases, stubbing would enable users to verify code that cannot currently be verified by Kani (or at least not within a reasonable resource bound). +Even without stubbing types, the ability to stub functions/methods can help provide verification-friendly abstractions for standard data structures. +For example, Issue 1673 suggests that some Kani proofs run more quickly if Vec::new is replaced with Vec::with_capacity; function stubbing would allow us to make this substitution everywhere in a codebase (and not just in the proof harness).

+

In what follows, we give an example of stubbing external code, using the annotations we propose in this RFC. +We are able to run this example on a modified version of Kani using a proof-of-concept MIR-to-MIR transformation implementing stubbing (the prototype does not support stub-related annotations; instead, it reads the stub mapping from a file). +This example stubs out a function that returns a random number. +This is the type of function that is commonly stubbed in other verification and program analysis projects, along with system calls, timer functions, logging calls, and deserialization methods---all of which we should be able to handle. +See the appendix at the end of this RFC for an extended example involving stubbing out a deserialization method.

+

Mocking randomization

+

The crate rand is widely used (150M downloads). +However, Kani cannot currently handle code that uses it (Kani users have run into this; see Issue 1727. +Consider this example:

+
#[cfg(kani)]
+#[kani::proof]
+fn random_cannot_be_zero() {
+    assert_ne!(rand::random::<u32>(), 0);
+}
+
+

For unwind values less than 2, Kani encounters an unwinding assertion error (there is a loop used to seed the random number generator); if we set an unwind value of 2, Kani fails to terminate within 5 minutes.

+

Using stubbing, we can specify that the function rand::random should be replaced with a mocked version:

+
#[cfg(kani)]
+fn mock_random<T: kani::Arbitrary>() -> T {
+    kani::any()
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::stub(rand::random, mock_random)]
+fn random_cannot_be_zero() {
+    assert_ne!(rand::random::<u32>(), 0);
+}
+
+

Under this substitution, Kani has a single check, which proves that the assertion can fail. Verification time is 0.02 seconds.

+

User experience

+

This feature is currently limited to stubbing functions and methods. +We anticipate that the annotations we propose here could also be used for stubbing types, although the underlying technical approach might have to change.

+

Stubs will be specified per harness; that is, different harnesses can use different stubs. +This is one of the main design points. +Users might want to mock the behavior of a function within one proof harness, and then mock it a different way for another harness, or even use the original function definition. +It would be overly restrictive to impose the same stub definitions across all proof harnesses. +A good example of this is compositional reasoning: in some harnesses, we want to prove properties of a particular function (and so want to use its actual implementation), and in other harnesses we want to assume that that function has those properties.

+

Users will specify stubs by attaching the #[kani::stub(<original>, <replacement>)] attribute to each harness function. +The arguments original and replacement give the names of functions/methods. +They will be resolved using Rust's standard name resolution rules; this includes supporting imports like use foo::bar as baz, as well as imports of multiple versions of the same crate (in which case a name would resolve to a function/method in a particular version). +The attribute may be specified multiple times per harness, so that multiple (non-conflicting) stub pairings are supported.

+

For example, this code specifies that the function mock_random should be used in place of the function rand::random and the function my_mod::bar should be used in place of the function my_mod::foo for the harness my_mod::my_harness:

+
#[cfg(kani)]
+fn mock_random<T: kani::Arbitrary>() -> T {
+    kani::any()
+}
+
+mod my_mod {
+
+    fn foo(x: u32) -> u32 { ... }
+
+    fn bar(x: u32) -> u32 { ... }
+
+    #[cfg(kani)]
+    #[kani::proof]
+    #[kani::stub(rand::random, super::mock_random)]
+    #[kani::stub(foo, bar)]
+    fn my_harness() { ... }
+
+}
+
+

We will support the stubbing of private functions and methods. +While this provides flexibility that we believe will be necessary in practice, it can also lead to brittle proofs: private functions/methods can change or disappear in even minor version upgrades (thanks to refactoring), and so proofs that depend on them might have a high maintenance burden. +In the documentation, we will discourage stubbing private functions/methods except if absolutely necessary.

+

Stub sets

+

As a convenience, we will provide a macro kani::stub_set that allows users to specify sets of stubs that can be applied to multiple harnesses:

+
kani::stub_set!(my_io_stubs(
+    stub(std::fs::read, my_read),
+    stub(std::fs::write, my_write),
+));
+
+

When declaring a harness, users can use the #[kani::use_stub_set(<stub_set_name>)] attribute to apply the stub set:

+
#[cfg(kani)]
+#[kani::proof]
+#[kani::use_stub_set(my_io_stubs)]
+fn my_harness() { ... }
+
+

The name of the stub set will be resolved through the module path (i.e., they are not global symbols), using Rust's standard name resolution rules.

+

A similar mechanism can be used to aggregate stub sets:

+
kani::stub_set!(all_my_stubs(
+    use_stub_set(my_io_stubs),
+    use_stub_set(my_other_stubs),
+));
+
+

Error conditions

+

Given a set of original-replacement pairs, Kani will exit with an error if

+
    +
  1. a specified original function/method does not exist;
  2. +
  3. a specified replacement stub does not exist;
  4. +
  5. the user specifies conflicting stubs for the same harness (e.g., if the same original function is mapped to multiple replacement functions); or
  6. +
  7. the signature of the replacement stub is not compatible with the signature of the original function/method (see next section).
  8. +
+

Stub compatibility and validation

+

When considering whether a function/method can be replaced with some given stub, we want to allow some measure of flexibility, while also ensuring that we can provide the user with useful feedback if stubbing results in misformed code. +We consider a stub and a function/method to be compatible if all the following conditions are met:

+
    +
  • They have the same number of parameters.
  • +
  • They have the same return type.
  • +
  • Each parameter in the stub has the same type as the corresponding parameter in the original function/method.
  • +
  • The stub must have the same number of generic parameters as the original function/method. +However, a generic parameter in the stub is allowed to have a different name than the corresponding parameter in the original function/method. +For example, the stub bar<A, B>(x: A, y: B) -> B is considered to have a type compatible with the function foo<S, T>(x: S, y: T) -> T.
  • +
  • The bounds for each type parameter don't need to match; however, all calls to the original function must also satisfy the bounds of the stub.
  • +
+

The final point is the most subtle. +We do not require that a type parameter in the signature of the stub implements the same traits as the corresponding type parameter in the signature of the original function/method. +However, Kani will reject a stub if a trait mismatch leads to a situation where a statically dispatched call to a trait method cannot be resolved during monomorphization. +For example, this restriction rules out the following harness:

+
fn foo<T>(_x: T) -> bool {
+    false
+}
+
+trait DoIt {
+    fn do_it(&self) -> bool;
+}
+
+fn bar<T: DoIt>(x: T) -> bool {
+    x.do_it()
+}
+
+#[kani::proof]
+#[kani::stub(foo, bar)]
+fn harness() {
+    assert!(foo("hello"));
+}
+
+

The call to the trait method DoIt::do_it is unresolvable in the stub bar when the type parameter T is instantiated with the type &str. +On the other hand, our approach provides some flexibility, such as allowing our earlier example of mocking randomization: both rand::random and my_random have the type () -> T, but in the first case T is restricted such that the type Standard implements Distribution<T>, whereas in the latter case T has to implement kani::Arbitrary. +This trait mismatch is allowed because at this call site T is instantiated with u32, which implements kani::Arbitrary.

+

Pedagogy

+

To teach this feature, we will update the documentation with a section on function and method stubbing, including simple examples showing how stubbing can help Kani handle code that currently cannot be verified, as well as a guide to best practices for stubbing.

+

Detailed design

+

We expect that this feature will require changes primarily to kani-compiler, with some less invasive changes to kani-driver. +We will modify kani-compiler to collects stub mapping information (from the harness attributes) before code generation. +Since stubs are specified on a per-harness basis, we need to generate multiple versions of code if all harnesses do not agree on their stub mappings; accordingly, we will update kani-compiler to generate multiple versions of code as appropriate. +To do the stubbing, we will plug in a new MIR-to-MIR transformation that replaces the bodies of specified functions with their replacements. +This can be achieved via rustc's query mechanism: if the user wants to replace foo with bar, then when the compiler requests the MIR for foo, we instead return the MIR for bar. +kani-compiler will also be responsible for checking for the error conditions previously enumerated.

+

We will also need to update the metadata that kani-compiler generates, so that it maps each harness to the generated code that has the right stub mapping for that harness (since there will be multiple versions of generated code). +The metadata will also list the stubs applied in each harness. +kani-driver will need to be updated to process this new type of metadata and invoke the correct generated code for each harness. +We can also update the results report to include the stubs that were used.

+

We anticipate that this design will evolve and be iterated upon.

+

Rationale and alternatives: user experience

+

Stubbing is a de facto necessity for verification tools, and the lack of stubbing has a negative impact on the usability of Kani.

+

Benefits

+
    +
  • Because stubs are specified by annotating the harness, the user is able to specify stubs for functions they do not have source access to (like library functions). +This contrasts with annotating the function to be replaced (such as with function contracts).
  • +
  • The current design provides the user with flexibility, as they can specify different sets of stubs to use for different harnesses. +This is important if users are trying to perform compositional reasoning using stubbing, since in some harnesses a function/method should be fully verified, in in other harnesses its behavior should be mocked.
  • +
  • The stub selections are located adjacent to the harness, which makes it easy to understand which replacements are going to happen for each harness.
  • +
+

Risks

+
    +
  • Users can always write stubs that do not correctly correspond to program behavior, and so a successful verification does not actually mean the program is bug-free. +This is similar to other specification bugs. +All the stubbing code will be available, so it is possible to inspect the assumptions it makes.
  • +
+

Comparison to function contracts

+
    +
  • In many cases, stubs are more user-friendly than contracts. +With contracts, it is sometimes necessary to explicitly provide information that is automatically captured in Rust (such as which memory is written). +Furthermore, contract predicates constitute a DSL of their own that needs to be learned; using stubbing, we can stick to using just Rust.
  • +
  • Function contracts sometimes come with a mechanism for verifying that a function satisfies its contract (for example, CBMC provides this). +While we do not plan to provide such a feature, it is possible to emulate this by writing proof harnesses comparing the behavior of the original function and the stub. +Furthermore, our approach provides additional flexibility, as it is not always actually desirable for a stub to be an overapproximation of the function (e.g., we might want the stub to exhibit a certain behavior within a particular harness) or to have a consistent behavior across all harnesses.
  • +
  • The currently proposed function contract mechanism does not provide a way to specify contracts on external functions. +In principle, it would be possible to extend it to do so. +Doing so would require some additional UX design decisions (e.g., "How do users specify this?"); with stubbing there does not need to be a distinction between local and external functions.
  • +
+

Alternative #1: Annotate stubbed functions

+

In this alternative, users add an attribute #[kani::stub_by(<replacement>)] to the function that should be replaced. +This approach is similar to annotating a function with a contract specifying its behavior (the stub acts like a programmatic contract). +The major downside with this approach is that it would not be possible to stub external code. We see this as a likely use case that needs to be supported: users will want to replace std library functions or functions from arbitrary external crates.

+

Alternative #2: Annotate stubs

+

In this alternative, users add an attribute #[kani::stub_of(<original>)] to the stub function itself, saying which function it replaces:

+
#[cfg(kani)]
+#[kani::stub_of(rand::random)]
+fn mock_random<T: kani::Arbitrary>() -> T { ... }
+
+

The downside is that this stub must be uniformly applied across all harnesses and the stub specifications might be spread out across multiple files. +It would also require an extra layer of indirection to use a function as a stub if the user does not have source code access to it.

+

Alternative #3: Annotate harnesses and stubs

+

This alternative combines the proposed solution and Alternative #2. +Users annotate the stub (as in Alternative #2) and specify for each harness which stubs to use using an annotation #[kani::use_stubs(<stub>+)] placed above the harness.

+

This could be combined with modules, so that a module can be used to group stubs together, and then harnesses could pull in all the stubs in the module:

+
#[cfg(kani)]
+mod my_stubs {
+
+  #[kani::stub_of(foo)]
+  fn stub1() { ... }
+
+  #[kani::stub_of(bar)]
+  fn stub2() { ... }
+
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::use_stubs(my_stubs)]
+fn my_harness() { ... }
+
+

The benefit is that stubs are specified per harness, and (using modules) it might be possible to group stubs together. +The downside is that multiple annotations are required and the stub mappings themselves are remote from the harness (at the harness you would know what stub is being used, but not what it is replacing). +There are also several issues that would need to be resolved:

+
    +
  • How do you mock multiple functions with the same stub? +(Say harness A wants to use stub1 to mock foo, and harness B wants to use stub1 to mock bar.)
  • +
  • How do you combine stub sets defined via modules? Would you use the module hierarchy?
  • +
  • If you use modules to define stub sets, are these modules regular modules or not? +In particular, given that modules can contain other constructs than functions, how should we interpret the extra stuff?
  • +
+

Alternative #4: Specify stubs in a file

+

One alternative would be to specify stubs in a file that is passed to kani-driver via a command line option. +Users would specify per-harness stub pairings in the file; JSON would be a possible format. +Using a file would eliminate the need for kani-compiler to do an extra pass to extract harness information from the Rust source code before doing code generation; the rest of the implementation would stay the same. +It would also allow the same harness to be run with different stub selections (by supplying a different file). +The disadvantage is that the stub selection is remote from the harness itself.

+

Rationale and alternatives: stubbing mechanism

+

Our approach is based on a MIR-to-MIR transformation. +Some advantages are that it operates over a relatively simple intermediate representation and rustc has good support for plugging in MIR-to-MIR transformations, so it would not require any changes to rustc itself. +At this stage of the compiler, names have been fully resolved, and there is no problem with swapping in the body of a function defined in one crate for a function defined in another. +Another benefit is that it should be possible to extend the compiler to integrate --concrete-playback with the abstractions (although doing so is out of scope for the current proposal).

+

The major downside with the MIR-to-MIR transformation is that it does not appear to be possible to stub types at that stage (there is no way to change the definition of a type through the MIR). +Thus, our proposed approach will not be a fully general stubbing solution. +However, it is technically feasible and relatively clean, and provides benefits over having no stubbing at all (as can be seen in the examples in the first part of this document).

+

Furthermore, it could be used as part of a portfolio of stubbing approaches, where users stub local types using conditional compilation (see Alternative #1), and Kani provides a modified version of the standard library with verification-friendly versions of types like std::vec::Vec.

+

Alternative #1: Conditional compilation

+

In this baseline alternative, we do not provide any stubbing mechanism at all. +Instead, users can effectively stub local code (functions, methods, and types) using conditional compilation. +For example, they could specify using #[cfg(kani)] to turn off the original definition and turn on the replacement definition when Kani is running, similarly to the ghost state approach taken in the Tokio Bytes proof.

+

The disadvantage with this approach is that it does not provide any way to stub external code, which is one of the main motivations of our proposed approach.

+

Alternative #2: Source-to-source transformation

+

In this alternative, we rewrite the source code before it even gets to the compiler. +The advantage with this approach is that it is very flexible, allowing us to stub functions, methods, and types, either by directly replacing them, or appending their replacements and injecting appropriate conditional compilation guards.

+

This approach entails less user effort than Alternative #1, but it has the same downside that it requires all source code to be available. +It also might be difficult to inject code in a way that names are correctly resolved (e.g., if the replacement code comes from a different crate). +Also, source code is difficult to work with (e.g., unexpanded macros).

+

On the last two points, we might be able to take advantage of an existing source analysis platform like rust-analyzer (which has facilities like structural search replace), but this would add more (potentially fragile) dependencies to Kani.

+

Alternative #3: AST-to-AST or HIR-to-HIR transformation

+

In this alternative, we implement stubbing by rewriting the AST or High-Level IR (HIR) of the program. +The HIR is a more compiler-friendly version of the AST; it is what is used for type checking. +To swap out a function, method, or type at this level, it looks like it would be necessary to add another pass to rustc that takes the initial AST/HIR and produces a new AST/HIR with the appropriate replacements.

+

The advantage with this approach is, like source transformations, it would be very flexible. +The downside is that it would require modifying rustc (as far as we know, there is not an API for plugging in a new AST/HIR pass), and would also require performing the transformations at a very syntactic level: although the AST/HIR would likely be easier to work with than source code directly, it is still very close to the source code and not very abstract. +Furthermore, provided we supported stubbing across crate boundaries, it seems like we would run into a sequencing issue: if we were trying to stub a function in a dependency, we might not know until after we have compiled that dependency that we need to modify its AST/HIR; furthermore, even if we were aware of this, the replacement AST/HIR code would not be available at that time (the AST/HIR is usually just constructed for the crate currently being compiled).

+

Open questions

+
    +
  • Would there ever be the need to stub a particular monomorphization of a function, as opposed to the polymorphic function?
  • +
  • How can the user verify that the stub is an abstraction of the original function/method? +Sometimes it might be important that a stub is an overapproximation or underapproximation of the replaced code. +One possibility would be writing proofs about stubs (possibly relating their behavior to that of the code they are replacing).
  • +
+

Limitations

+
    +
  • Our proposed approach will not work with --concrete-playback (for now).
  • +
  • We are only able to apply abstractions to some dependencies if the user enables the MIR linker.
  • +
+

Future possibilities

+
    +
  • +

    It would increase the utility of stubbing if we supported stubs for types. +The source code annotations could likely stay the same, although the underlying technical approach performing these substitutions might be significantly more complex.

    +
  • +
  • +

    It would probably make sense to provide a library of common stubs for users, since many applications might want to stub the same functions and mock the same behaviors (e.g., rand::random can be replaced with a function returning kani::any).

    +
  • +
  • +

    We could provide special classes of stubs that are likely to come up in practice:

    +
      +
    • unreachable: assert the function is unreachable.
    • +
    • havoc_locals: return nondeterministic values and assign nondeterministic values to all mutable arguments.
    • +
    • havoc: similar to havoc_locals but also assign nondeterministic values to all mutable global variables.
    • +
    • uninterpret: treat function as an uninterpreted function.
    • +
    +
  • +
  • +

    How can we provide a good user experience for accessing private fields of self in methods? +It is possible to do so using std::mem::transmute (see below); this is clunky and error-prone, and it would be good to provide better support for users.

    +
    struct Foo {
    +    x: u32,
    +}
    +
    +impl Foo {
    +    pub fn m(&self) -> u32 {
    +        0
    +    }
    +}
    +
    +struct MockFoo {
    +    pub x: u32,
    +}
    +
    +fn mock_m(foo: &Foo) {
    +    let mock: &MockFoo = unsafe { std::mem::transmute(foo) };
    +    return mock.x;
    +}
    +
    +#[cfg(kani)]
    +#[kani::proof]
    +#[kani::stub(Foo::m, mock_m)]
    +fn my_harness() { ... }
    +
    +
  • +
+

Appendix: an extended example

+

In this example, we mock a serde_json (96M downloads) deserialization method so that we can prove a property about the following Firecracker function that parses a configuration from some raw data:

+
fn parse_put_vsock(body: &Body) -> Result<ParsedRequest, Error> {
+    METRICS.put_api_requests.vsock_count.inc();
+    let vsock_cfg = serde_json::from_slice::<VsockDeviceConfig>(body.raw()).map_err(|err| {
+        METRICS.put_api_requests.vsock_fails.inc();
+        err
+    })?;
+
+    // Check for the presence of deprecated `vsock_id` field.
+    let mut deprecation_message = None;
+    if vsock_cfg.vsock_id.is_some() {
+        // vsock_id field in request is deprecated.
+        METRICS.deprecated_api.deprecated_http_api_calls.inc();
+        deprecation_message = Some("PUT /vsock: vsock_id field is deprecated.");
+    }
+
+    // Construct the `ParsedRequest` object.
+    let mut parsed_req = ParsedRequest::new_sync(VmmAction::SetVsockDevice(vsock_cfg));
+    // If `vsock_id` was present, set the deprecation message in `parsing_info`.
+    if let Some(msg) = deprecation_message {
+        parsed_req.parsing_info().append_deprecation_message(msg);
+    }
+
+    Ok(parsed_req)
+}
+
+

We manually mocked some of the Firecracker types with simpler versions to reduce the number of dependencies we had to pull in (e.g., we removed some enum variants, unused struct fields). +With these changes, we were able to prove that the configuration data has a vsock ID if and only if the parsing metadata includes a deprecation message:

+
#[cfg(kani)]
+fn get_vsock_device_config(action: RequestAction) -> Option<VsockDeviceConfig> {
+    match action {
+        RequestAction::Sync(vmm_action) => match *vmm_action {
+            VmmAction::SetVsockDevice(dev) => Some(dev),
+            _ => None,
+        },
+        _ => None,
+    }
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::unwind(2)]
+#[kani::stub(serde_json::deserialize_slice, mock_deserialize)]
+fn test_deprecation_vsock_id_consistent() {
+    // We are going to mock the parsing of this body, so might as well use an empty one.
+    let body: Vec<u8> = Vec::new();
+    if let Ok(res) = parse_put_vsock(&Body::new(body)) {
+        let (action, mut parsing_info) = res.into_parts();
+        let config = get_vsock_device_config(action).unwrap();
+        assert_eq!(
+            config.vsock_id.is_some(),
+            parsing_info.take_deprecation_message().is_some()
+        );
+    }
+}
+
+

Crucially, we did this by stubbing out serde_json::from_slice and replacing it with our mock version below, which ignores its input and creates a "symbolic" configuration struct:

+
#[cfg(kani)]
+fn symbolic_string(len: usize) -> String {
+    let mut v: Vec<u8> = Vec::with_capacity(len);
+    for _ in 0..len {
+        v.push(kani::any());
+    }
+    unsafe { String::from_utf8_unchecked(v) }
+}
+
+#[cfg(kani)]
+fn mock_deserialize(_data: &[u8]) -> serde_json::Result<VsockDeviceConfig> {
+    const STR_LEN: usize = 1;
+    let vsock_id = if kani::any() {
+        None
+    } else {
+        Some(symbolic_string(STR_LEN))
+    };
+    let guest_cid = kani::any();
+    let uds_path = symbolic_string(STR_LEN);
+    let config = VsockDeviceConfig {
+        vsock_id,
+        guest_cid,
+        uds_path,
+    };
+    Ok(config)
+}
+
+

The proof takes 170 seconds to complete (using Kissat as the backend SAT solver for CBMC).

+
+
+

Summary

+

A new Kani API that allows users to check that a certain condition can occur at a specific location in the code.

+

User Impact

+

Users typically want to gain confidence that a proof checks what it is supposed to check, i.e. that properties are not passing vacuously due to an over-constrained environment.

+

A new Kani macro, cover will be created that can be used for checking that a certain condition can occur at a specific location in the code. +The purpose of the macro is to verify, for example, that assumptions are not ruling out those conditions, e.g.:

+
let mut v: Vec<i32> = Vec::new();
+let len: usize = kani::any();
+kani::assume(len < 5);
+for _i in 0..len {
+    v.push(kani::any());
+}
+// make sure we can get a vector of length 5
+kani::cover!(v.len() == 5);
+
+

This is typically used to ensure that verified checks are not passing vacuously, e.g. due to overconstrained pre-conditions.

+

The special case of verifying that a certain line of code is reachable can be achieved using kani::cover!() (which is equivalent to cover!(true)), e.g.

+
match x {
+    val_1 => ...,
+    val_2 => ...,
+    ...
+    val_i => kani::cover!(), // verify that `x` can take the value `val_i`
+}
+
+

Similar to Rust's assert macro, a custom message can be specified, e.g.

+
kani::cover!(x > y, "x can be greater than y");
+
+

User Experience

+

The cover macro instructs Kani to find at least one possible execution that satisfies the specified condition at that line of code. If no such execution is possible, the check is reported as unsatisfiable.

+

Each cover statement will be reported as a check whose description is cover condition: cond and whose status is:

+
    +
  • SATISFIED (green): if Kani found an execution that satisfies the condition.
  • +
  • UNSATISFIABLE (yellow): if Kani proved that the condition cannot be satisfied.
  • +
  • UNREACHABLE (yellow): if Kani proved that the cover statement itself cannot be reached.
  • +
+

For example, for the following cover statement:

+
kani::cover!(a == 0);
+
+

An example result is:

+
Check 2: main.cover.2
+         - Status: SATISFIED
+         - Description: "cover condition: a == 0"
+         - Location: foo.rs:9:5 in function main
+
+

Impact on Overall Verification Status

+

By default, unsatisfiable and unreachable cover checks will not impact verification success or failure. +This is to avoid getting verification failure for harnesses for which a cover check is not relevant. +For example, on the following program, verification should not fail for another_harness_that_doesnt_call_foo because the cover statement in foo is unreachable from it.

+
[kani::proof]
+fn a_harness_that_calls_foo() {
+    foo();
+}
+
+#[kani::proof]
+fn another_harness_that_doesnt_call_foo() {
+    // ...
+}
+
+fn foo() {
+    kani::cover!( /* some condition */);
+}
+
+

The --fail-uncoverable option will allow users to fail the verification if a cover property is unsatisfiable or unreachable. +This option will be integrated within the framework of Global Conditions, which is used to define properties that depend on other properties.

+

Using the --fail-uncoverable option will enable the global condition with name fail_uncoverable. +Following the format for global conditions, the outcome will be one of the following:

+
    +
  1. - fail_uncoverable: FAILURE (expected all cover statements to be satisfied, but at least one was not)
  2. +
  3. - fail_uncoverable: SUCCESS (all cover statements were satisfied as expected)
  4. +
+

Note that the criteria to achieve a SUCCESS status depends on all properties of the "cover" class having a SATISFIED status. +Otherwise, we return a FAILURE status.

+

Inclusion in the Verification Summary

+

Cover checks will be reported separately in the verification summary, e.g.

+
SUMMARY:
+ ** 1 of 206 failed (2 unreachable)
+ Failed Checks: assertion failed: x[0] == x[1]
+
+ ** 30 of 35 cover statements satisfied (1 unreachable) <--- NEW
+
+

In this example, 5 of the 35 cover statements were found to be unsatisfiable, and one of those 5 is additionally unreachable.

+

Interaction with Other Checks

+

If one or more unwinding assertions fail or an unsupported construct is found to be reachable (which indicate an incomplete path exploration), and Kani found the condition to be unsatisfiable or unreachable, the result will be reported as UNDETERMINED.

+

Detailed Design

+

The implementation will touch the following components:

+
    +
  • Kani library: The cover macro will be added there along with a cover function with a rustc_diagnostic_item
  • +
  • kani-compiler: The cover function will be handled via a hook and codegen as two assertions (cover(cond) will be codegen as __CPROVER_assert(false); __CPROVER_assert(!cond)). +The purpose of the __CPROVER_assert(false) is to determine whether the cover statement is reachable. +If it is, the second __CPROVER_assert(!cond) indicates whether the condition is satisfiable or not.
  • +
  • kani-driver: The CBMC output parser will extract cover properties through their property class, and their result will be set based on the result of the two assertions: +
      +
    • The first (reachability) assertion is proven: report as FAILURE (UNREACHABLE)
    • +
    • The first assertion fails, and the second one is proven: report as FAILURE to indicate that the condition is unsatisfiable
    • +
    • The first assertion fails, and the second one fails: report as SUCCESS to indicate that the condition is satisfiable
    • +
    +
  • +
+

Rationale and alternatives

+
    +
  • +

    What are the pros and cons of this design? +CBMC has its own cover API (__CPROVER_cover), for which SUCCESS is reported if an execution is found, and FAILURE is reported otherwise. +However, using this API currently requires running CBMC in a separate "cover" mode. +Having to run CBMC in that mode would complicate the Kani driver as it will have to perform two CBMC runs, and then combine their results into a single report. +Thus, the advantage of the proposed design is that it keeps the Kani driver simple. +In addition, the proposed solution does not depend on a feature in the backend, and thus will continue to work if we were to integrate a different backend.

    +
  • +
  • +

    What is the impact of not doing this? +The current workaround to accomplish the same effect of verifying that a condition can be covered is to use assert!(!cond). +However, if the condition can indeed be covered, verification would fail due to the failure of the assertion.

    +
  • +
+

Open questions

+
    +
  • ~Should we allow format arguments in the macro, e.g. kani::cover!(x > y, "{} can be greater than {}", x, y)? +Users may expect this to be supported since the macro looks similar to the assert macro, but Kani doesn't include the formatted string in the message description, since it's not available at compile time.~ +
      +
    • For now, this macro will not accept format arguments, since this +is not well handled by Kani. +This is an extesion to this API that can be easily added later on if Kani +ever supports runtime formatting.
    • +
    +
  • +
+

Other Considerations

+

We need to make sure the concrete playback feature can be used with cover statements that were found to be coverable.

+

Future possibilities

+

The new cover API subsumes the current kani::expect_fail function. +Once it's implemented, we should be able to get rid of expect_fail, and all the related code in compiletest that handles the EXPECTED FAILURE message in a special manner.

+
+
+

Summary

+

A new option that allows users to verify programs without unwinding loops by synthesizing loop contracts for those loops.

+

User Impact

+

Currently Kani does not support verification on programs with unbounded control flow (e.g. loops with dynamic bounds). +Kani unrolls all unbounded loops until a global threshold (unwinding number) specified by the user and then verifies this unrolled program, which limits the set of programs it can verify.

+

A new Kani flag --synthesize-loop-contracts will be created that can be used to enable the goto-level loop-contract synthesizer goto-synthesizer. +The idea of loop contracts is, instead of unwinding loops, we abstract those loops as non-loop structures that can cover arbitrary iterations of the loops. +The loop contract synthesizer, when enabled, will attempt to synthesize loop contracts for all loops. +CBMC can then apply the synthesized loop contracts and verify the program without unwinding any loop. +So, the synthesizer will help to verify the programs that require Kani to unwind loops for a very large number of times to cover all iterations.

+

For example, the number of executed iterations of the loop in the following harness is dynamically bounded by an unbounded variable y 1. +Only an unwinding value of i32::MAX can guarantee to cover all iterations of the loop, and hence satisfy the unwinding assertions. +Unwinding the loop an i32::MAX number of times will result in a too large goto program to be verified by CBMC.

+
#[kani::proof]
+fn main() {
+    let mut y: i32 = kani::any_where(|i| *i > 0);
+
+    while y > 0 {
+        y = y - 1;
+    }
+    assert!(y == 0);
+}
+
+

With the loop-contract synthesizer, Kani can synthesize the loop invariant y >= 0, with which it can prove the post-condition y == 0 without unwinding the loop.

+

Also, loop contracts could improve Kani’s verification time since all loops will be abstracted to a single iteration, as opposed to being unwound a large number of iterations. +For example, we can easily find out that the following loop is bounded by an unwinding value of 5000. +Kani can verify the program in a few minutes by unwinding the loop 5000 times. +With loop contracts, we only need to verify the single abstract iteration of the loop, which leads to a smaller query. +As a result, Kani with the synthesizer can verify the program in a few seconds.

+
#[kani::proof]
+#[kani::unwind(5000)]
+fn main() {
+    let mut y: i32 = 5000;
+
+    while y > 0 {
+        y = y - 1;
+    }
+    assert!(y == 0);
+}
+
+

The goto-synthesizer is an enumeration-based synthesizer. +It enumerates candidate invariants from a pre-designed search space described by a given regular tree grammar and verifies if the candidate is an inductive invariant. +Therefore it has the following limitations:

+
    +
  1. the search space is not complete, so it may fail to find a working candidate. The current search space consists of only conjunctions of linear inequalities built from the variables in the loop, which is not expressive enough to capture all loop invariants. +For example, the loop invariant a[i] == 0 contains an array access and cannot be captured by the current search space. +However, we can easily extend the search space to include more complex expressions with the cost of an exponential increase of the running time of the synthesizer.
  2. +
  3. the synthesizer suffers from the same limitation as the loop contract verification in CBMC. For example, it does not support unbounded quantifiers, or dynamic allocations in the loop body.
  4. +
+

User Experience

+

Users will be able to use the new command-line flag --synthesize-loop-contracts to run the synthesizer, which will attempt to synthesize loop contracts, and verify programs with the synthesized loop contracts.

+

Limit Resource Used by Synthesizer for Termination

+

Without a resource limit, an enumerative synthesizer may run forever to exhaust a search space consisting of an infinite number of candidates, especially when there is no solution in the search space. +So, for the guarantee of termination, we provide users options: --limit-synthesis-time T to limit the running time of the synthesizer to be less than T seconds.

+

Output of Kani when the Synthesizer is Enabled

+

When the flag --synthesize-loop-contracts is provided, Kani will report different result for different cases

+
    +
  1. When there exists some loop invariant in the candidate space with which all assertions can be proved, Kani will synthesize the loop contracts, verify the program with the synthesized loop contracts, and report verification SUCCESS;
  2. +
  3. When no working candidate has been found in the search space within the specified limits, Kani will report the verification result with the best-effort-synthesized loop contracts. +Note that as loop contracts are over-approximations of the loop, the violated assertions in this case may be spurious. +So we will report the violated assertions as UNDETERMINED instead of FAILED.
  4. +
+

A question about how do we print the synthesized loop contracts when users request is discussed in Open question.

+

Detailed Design

+

The synthesizer goto-synthesizer is implemented in the repository of CBMC, takes as input a goto binary, and outputs a new goto binary with the synthesized loop contracts applied. +Currently, Kani invokes goto-instrument to instrument the goto binary main.goto into a new goto binary main_instrumented.goto, and then invokes cbmc on main_instrumented.goto to get the verification result. +The synthesis will happen between calling goto-instrument and calling cbmc. +That is, we invoke goto-synthesizer on main_instrumented.goto to produce a new goto binary main_synthesized.goto, and then call cbmc on main_synthesized.goto instead.

+

When invoking goto-synthesizer, we pass the following parameters to it with the flags built in goto-synthesizer:

+
    +
  • the resource limit of the synthesis;
  • +
  • the solver options to specify what SAT solver we use to verify invariant candidates.
  • +
+

The enumerator used in the synthesizer enumerates candidates from the language of the following grammar template.

+
NT_Bool -> NT_Bool && NT_Bool | NT_int == NT_int 
+            | NT_int <= NT_int | NT_int < NT_int 
+            | SAME_OBJECT(terminals_ptr, terminals_ptr)
+            
+NT_int  -> NT_int + NT_int | terminals_int | LOOP_ENTRY(terminals_int)
+            | POINTER_OFFSET(terminals_ptr) | OBJECT_SIZE(terminals_ptr)
+            | POINTER_OFFSET(LOOP_ENTRY(terminals_ptr)) | 1
+
+

where terminals_ptr are all pointer variables in the scope, and terminal_int are all integer variables in the scope. +For every candidate invariant, goto-synthesizer applies it to the GOTO program and runs CBMC to verify the program.

+
    +
  • If all checks in the program pass, goto-synthesizer returns it as a solution.
  • +
  • If the inductive checks pass but some of the other checks fail, the candidate invariant is inductive. +We keep it as an inductive invariant clause.
  • +
  • If the inductive checks fail, we discard the candidate. +When the resource limit is reached, goto-synthesizer returns the conjunction of all inductive clauses as the best-effort-synthesized loop contracts.
  • +
+

We use the following example to illustrate how the synthesizer works.

+
#[kani::proof]
+fn main() {
+    let mut y: i32 = kani::any_where(|i| *i > 0);
+
+    while y > 0 {
+        y = y - 1;
+    }
+    assert!(y == 0);
+}
+
+

As there is only one variable y in the scope, the grammar template above will be instantiated to the following grammar

+
NT_Bool -> NT_Bool && NT_Bool | NT_int == NT_int 
+            | NT_int <= NT_int | NT_int < NT_int 
+NT_int  -> NT_int + NT_int | y | LOOP_ENTRY(y) | 1
+
+

The synthesizer will enumerate candidates derived from NT_Bool in the following order.

+
y == y
+y == LOOP_ENTRY(y)
+y == 1
+...
+1 <= y + 1
+...
+
+

The synthesizer then verifies with CBMC if the candidate is an inductive invariant that can be used to prove the post-condition y == 0. +For example, the candidate y == y is verified to be an inductive invariant, but cannot be used to prove the post-condition y == 0. +The candidate y == 1 is not inductive. +The synthesizer rejects all candidates until it finds the candidate 1 <= y + 1, which can be simplified to y >= 0. +y >= 0 is an inductive invariant that can be used to prove the post-condition. +So the synthesizer will return y >= 0 and apply it to the goto model to get main_synthesized.goto.

+

For assign clauses, the synthesizer will first use alias analysis to determine an initial set of assign targets. +During the following iteration, if any assignable-check is violated, the synthesizer will extract the assign target from the violated check.

+

Then Kani will call cbmc on main_synthesized.goto to verify the program with the synthesized loop contracts.

+

Rationale and alternatives

+
    +
  • Different candidate space. +The candidate grammar introduced above now only contains a restricted set of operators, which works well for array-manipulating programs with only pointer-checks instrumented by goto-instrument, but probably not enough for other user-written checks. +We may want to include array-indexing, pointer-dereference, or other arithmetic operators in the candidate grammar for synthesizing a larger set of loop invariants. +However, there is a trade-off between the size of candidate we enumerate and the running time of the enumeration. +We will collect more data to decide what operators we should include in the candidate grammar. +Once we decide more kinds of candidate grammars, we will provide users options to choose which candidate grammar they want to use.
  • +
+

Open questions

+

How does the synthesizer work with unwinding numbers? +There may exist some loops for which the synthesizer cannot find loop contracts, but some small unwinding numbers are enough to cover all executions of the loops. +In this case, we may want to unwind some loops in the program while synthesizing loop contracts for other loops. +It requires us to have a way to identify and specify which loops we want to unwind.

+

In C programs, we identify loops by the loop ID, which is a pair (function name, loop number). +However, in Rust programs, loops are usually in library functions such as Iterator::for_each. +And a library function may be called from different places in the program. +We may want to unwind the loop in some calls but not in other calls.

+

How do we output the synthesized loop contracts? +To better earn users' trust, we want to be able to report what loop contracts we synthesized and used to verify the given programs. +Now goto-synthesizer can dump the synthesized loop contracts into a JSON file. +Here is an example of the dumped loop contracts. +It contains the location of source files of the loops, the synthesized invariant clauses and assign clauses for loops identified by loop numbers.

+
{
+    "sources": [ "/Users/qinhh/Repos/playground/kani/synthesis/base_2/test.rs" ],
+    "functions": [
+      {
+        "main": [ "loop 1 invariant y >= 0", 
+                  "loop 1 assigns var_9,var_10,var_11,x,y,var_12" ]
+      }
+    ],
+    "output": "stdout"
+}
+
+

There are two challenges here if we want to also dump synthesized loop contracts in Kani.

+
    +
  1. We need to have a consistent way to identify loops.
  2. +
  3. We need to dump loop invariants in rust instead of c.
  4. +
  5. There are many auxiliary variables we added in Kani-compiled GOTO, such as var_9, var_10, var_11, and var_12 in the above JSON file. +We need to translate them back to the original variables they represent.
  6. +
+

Future possibilities

+

User-provided loop contracts. +If we have a good answer for how to identify loops and dump synthesized loop contracts, we could probably also allow users to provide the loop contracts they wrote to Kani, and verify programs with user-provided loop contracts.

+

When users want to unwind some loops, we can also introduce macros to enable/disable unwinding for certain block of code.

+
#[kani::proof]
+#[kani::unwind(10)]
+fn check() {
+    // unwinding starts as enabled, so all loops in this code block will be unwound to 10
+    #[kani::disable_unwinding]
+    // unwinding is disabled for all loops in this block of code
+    #[kani::enable_unwinding]
+    // it is enabled in this block of code until the end of the program
+}
+
+

Invariant caching. +The loop invariant could be broken when users modify their code. +However, we could probably cache previously working loop invariants and attempt to reuse them when users modify their code. +Even if the cached loop invariants are not enough to prove the post-condition, they could still be used as a starting point for the synthesizer to find new loop invariants.

+
1 +

We say an integer variable is unbounded if there is no other bound on its value besides the width of its bit-vector representation.

+
+
+ +
+

Summary

+

Users may want to express that a verification harness should panic. +This RFC proposes a new harness attribute #[kani::should_panic] that informs Kani about this expectation.

+

User Impact

+

Users may want to express that a verification harness should panic. +In general, a user adding such a harness wants to demonstrate that the verification fails because a panic is reachable from the harness.

+

Let's refer to this concept as negative verification, +so the relation with negative testing becomes clearer. +Negative testing can be exercised in Rust unit tests using the #[should_panic] attribute. +If the #[should_panic] attribute is added to a test, cargo test will check that the execution of the test results in a panic. +This capability doesn't exist in Kani at the moment, but it would be useful for the same reasons +(e.g., to show that invalid inputs result in verification failures, or increase the overall verification coverage).

+

We propose an attribute that allows users to exercise negative verification in Kani.

+

We also acknowledge that, in other cases, users may want to express more granular expectations for their harnesses. +For example, a user may want to specify that a given check is unreachable from the harness. +An ergonomic mechanism for informing Kani about such expectations is likely to require other improvements in Kani (a comprehensive classification for checks reported by Kani, a language to describe expectations for checks and cover statements, and general output improvements). +Moving forward, we consider that such a mechanism and this proposal solve different problems, so they don't need to be discussed together. +This is further discussed in the rationale and alternatives and future possibilities sections.

+

User Experience

+

The scope of this functionality is limited to the overall verification result. +The rationale section discusses the granularity of failures, and how this attribute could be extended.

+

Single Harness

+

Let's look at this code:

+
struct Device {
+    is_init: bool,
+}
+
+impl Device {
+    fn new() -> Self {
+        Device { is_init: false }
+    }
+
+    fn init(&mut self) {
+        assert!(!self.is_init);
+        self.is_init = true;
+    }
+}
+
+#[kani::proof]
+fn cannot_init_device_twice() {
+    let mut device = Device::new();
+    device.init();
+    device.init();
+}
+
+

This is what a negative harness may look like. +The user wants to verify that calling device.init() more than once should result in a panic.

+
+

NOTE: We could convert this into a Rust unit test and add the #[should_panic] attribute to it. +However, there are a few good reasons to have a verification-specific attribute that does the same:

+
    +
  1. To ensure that other unexpected behaviors don't occur (e.g., overflows).
  2. +
  3. Because #[should_panic] cannot be used if the test harness contains calls to Kani's API.
  4. +
  5. To ensure that a panic still occurs after stubbing out code which is expected to panic.
  6. +
+
+

Currently, this example produces a VERIFICATION:- FAILED result. +In addition, it will return a non-successful code.

+
#[kani::proof]
+#[kani::should_panic]
+fn cannot_init_device_twice() {
+    let mut device = Device::new();
+    device.init();
+    device.init();
+}
+
+

Since we added #[kani::should_panic], running this example would produce a successful code.

+

Now, we've considered two ways to represent this result in the verification output. +Note that it's important that we provide the user with this feedback:

+
    +
  1. (Expectation) Was Kani expecting the harness to panic?
  2. +
  3. (Outcome): What's the actual result that Kani produced after the analysis? +This will avoid a potential scenario where the user doesn't know for sure if the attribute has had an effect when verifying the harness.
  4. +
+

Therefore, the representation must make clear both the expectation and the outcome. +Below, we show how we'll represent this result.

+ +

The #[kani::should_panic] attribute essentially behaves as a property that depends on other properties. +This makes it well-suited for integration within the framework of Global Conditions.

+

Using the #[kani::should_panic] attribute will enable the global condition with name should_panic. +Following the format for global conditions, the outcome will be one of the following:

+
    +
  1. - `should_panic`: FAILURE (encountered no panics, but at least one was expected) if there were no failures.
  2. +
  3. - `should_panic`: FAILURE (encountered failures other than panics, which were unexpected) if there were failures but not all them had prop.property_class() == "assertion".
  4. +
  5. - `should_panic`: SUCCESS (encountered one or more panics as expected) otherwise.
  6. +
+

Note that the criteria to achieve a SUCCESS status depends on all failed properties having the property class "assertion". +If they don't, then the failed properties may contain UB, so we return a FAILURE status instead.

+

Multiple Harnesses

+

When there are multiple harnesses, we'll implement the single-harness changes in addition to the following ones. +Currently, a "Summary" section appears1 after reporting the results for each harness:

+
Verification failed for - harness3
+Verification failed for - harness2
+Verification failed for - harness1
+Complete - 0 successfully verified harnesses, 3 failures, 3 total.
+
+

Harnesses marked with #[kani::should_panic] won't show unless the expected result was different from the actual result. +The summary will consider harnesses that match their expectation as "successfully verified harnesses".

+

Therefore, if we added #[kani::should_panic] to all harnesses in the previous example, we'd see this output:

+
Complete - 3 successfully verified harnesses, 0 failures, 3 total.
+
+

Multiple panics

+

In a verification context, an execution can branch into multiple executions that depend on a condition. +This may result in a situation where different panics are reachable, as in this example:

+
#[kani::proof]
+#[kani::should_panic]
+fn branch_panics() {
+    let b: bool = kani::any();
+
+    do_something();
+
+    if b {
+        call_panic_1(); // leads to a panic-related failure
+    } else {
+        call_panic_2(); // leads to a different panic-related failure
+    }
+}
+
+

Note that we could safeguard against these situations by checking that only one panic-related failure is reachable. +However, users have expressed that a coarse version (i.e., checking that at least one panic can be reached) is preferred. +Users also anticipate that #[kani::should_panic] will be used to exercise smoke testing in many cases. +Additionally, restricting #[kani::should_panic] to the verification of single panic-related failures could be confusing for users and reduce its overall usefulness.

+

Availability

+

This feature will only be available as an attribute. +That means this feature won't be available as a CLI option (i.e., --should-panic). +There are good reasons to avoid the CLI option:

+
    +
  • It'd make the design and implementation unnecessarily complex.
  • +
  • It'd only be useful when combined with --harness to filter negative harnesses.
  • +
  • We could have trouble extending its functionality (see Future possibilities for more details).
  • +
+

Pedagogy

+

The #[kani::should_panic] attribute will become one of the most basic attributes in Kani. +As such, it'll be mentioned in the tutorial and added to the dedicated section planned in #2208.

+

In general, we'll also advise against negative verification when a harness can be written both as a regular (positive) harness and a negative one. +The feature, as it's presented in this proposal, won't allow checking that the panic failure is due to the panic we expected. +So there could be cases where the panic changes, but it goes unnoticed while running Kani. +Because of that, it'll preferred that users write positive harnesses instead.

+

Detailed Design

+

At a high level, we expect modifications in the following components:

+
    +
  • kani-compiler: Changes required to (1) process the new attribute, and (2) extend HarnessMetadata with a should_panic: bool field.
  • +
  • kani-driver: Changes required to (1) edit information about harnesses printed by kani-driver, (2) edit verification output when post-processing CBMC verification results, and (3) return the appropriate exit status after post-processing CBMC verification results.
  • +
+

We don't expect these changes to require new dependencies. +Besides, we don't expect these changes to be updated unless we decide to extend the attribute with further fields (see Future possibilities for more details).

+

Rationale and alternatives

+

This proposal would enable users to exercise negative verification with a relatively simple mechanism. +Not adding such a mechanism could impact Kani's usability by limiting the harnesses that users can write.

+

Alternative #1: Generic failures

+

This proposal doesn't consider generic failures but only panics. +In principle, it's not clear that a mechanism for generic failures would be useful. +Such a mechanism would allow users to expect UB in their harness, but there isn't a clear motivation for doing that.

+

Alternative #2: Name

+

We have considered two alternatives for the "expectation" part of the attribute's name: should and expect. +We avoid expect altogether for two reasons:

+
    +
  • We may consider adding the expected argument to #[kani::should_panic].
  • +
  • We may consider a more granular approach to indicate expectations regarding individual checks and cover statements in the future. One possible name for the attribute is #[kani::expect].
  • +
  • We heavily use this word for testing in Kani: there is an expected mode, which works with *.expected files. Other modes also use such files.
  • +
+

Alternative #3: The expected argument

+

We could consider an expected argument, similar to the #[should_panic] attribute. +To be clear, the #[should_panic] attribute may receive an argument expected which allows users to specify the expected panic string:

+
    #[test]
+    #[should_panic(expected = "Divide result is zero")]
+    fn test_specific_panic() {
+        divide_non_zero_result(1, 10);
+    }
+
+

In principle, we anticipate that we'll extend this proposal to include the expected argument at some point. +The implementation could compare the expected string against the panic string.

+

At present, the only technical limitation is that panic strings printed in Kani aren't formatted. +One option is to use substrings to compare. +However, the long-term solution is to use concrete playback to replay the panic and match against the expected panic string. +By doing this, we would achieve feature parity with Rust's #[should_panic].

+

Alternative #4: Granularity

+

As mentioned earlier, users may want to express more granular expectations for their harnesses.

+

There could be problems with this proposal if we attempt to do both:

+
    +
  • What if users don't want to only check for failures (e.g., reachability)?
  • +
  • In the previous case, would they expect the overall verification to fail or not?
  • +
  • How do we want these expectations to be declared?
  • +
+

We don't have sufficient data about the use-case considered in this alternative. +This proposal can also contribute to collect this data: once users can expect panics, they may want to expect other things.

+

Alternative #5: Kani API

+

This functionality could be part of the Kani API instead of being an attribute. +For example, some contributors proposed a function that takes a predicate closure to filter executions and check that they result in a panic.

+

However, such a function couldn't be used in external code, limiting its usability to the user's code.

+

Open questions

+

Once the feature is available, it'd be good to gather user feedback to answer these questions:

+
    +
  • Do we need a mechanism to express more granular expectations?
  • +
  • If we need the mechanism in (2), do we really want to collapse them into one feature?
  • +
+

Resolved questions

+
    +
  • What is the best representation to use for this feature? A representation that changes the overall result seems to be preferred, according to feedback we received during a discussion.
  • +
  • Do we want to extend #[kani::should_panic] with an expected field? Yes, but not in this version.
  • +
  • Do we want to allow multiple panic-related failures with #[kani::should_panic]? Yes (this is now discussed in User Experience).
  • +
+

Future possibilities

+
    +
  • The attribute could be an argument to kani::proof (#[kani::proof(should_panic)] reads very well).
  • +
  • Add an expected argument to #[kani::should_panic], and replay the harness with concrete playback to get the actual panic string.
  • +
+
2 +

Double negation may not be the best representation, but it's at least accurate with respect to the original result.

+
+
1 +

This summary is printed in both the default and terse outputs.

+
+
+
+

Summary

+

Provide a standard option for users to enable experimental APIs and features in Kani, +and ensure that those APIs are off by default.

+

User Impact

+

Add an opt-in model for users to try experimental APIs. +The goal is to enable users to try features that aren't stable yet, +which allow us to get valuable feedback during the development of new features and APIs.

+

The opt-in model empowers the users to control when some instability is acceptable, +which makes Kani UX more consistent and safe.

+

Currently, each new unstable feature will introduce a new switch, some of them will look like --enable-<feature>, +while others will be a plain switch which allows further feature configuration --<feature-config>=[value]. +For example, we today have the following unstable switches --enable-stubbing, --concrete-playback, --gen-c. +In all cases, users are still required to provide the additional --enable-unstable option. +Some unstable features are included in the --help section, and only a few mention the requirement +to include --enable-unstable. There is no way to list all unstable features. +The transition to stable switches is also ad-hoc.

+

In order to reduce friction, we will also standardize how users opt-in to any Kani unstable feature. +We will use similar syntax to the one used by the Rust compiler and Cargo. +As part of this work, we will also deprecate and remove --enable-unstable option.

+

Note that although Kani is still on v0, which means that everything is somewhat unstable, +this allow us to set different bars when it comes to what kind of changes is expected, +as well as what kind of support we will provide for a feature.

+

User Experience

+

Users will have to invoke Kani with:

+
-Z <feature_identifier>
+
+

in order to enable any unstable feature in Kani, including unstable APIs in the Kani library. +For unstable command line options, we will add -Z unstable-options, similar to the Rust compiler. +E.g.:

+
-Z unstable-options --concrete-playback=print
+
+

Users will also be able to enable unstable features in their Cargo.toml in the unstable table +under kani table. E.g:

+
[package.metadata.kani.unstable]
+unstable-options = true
+
+[workspace.metadata.kani]
+flags = { concrete-playback = true }
+unstable = { unstable-options = true }
+
+

In order to mark an API as unstable, we will add the following attribute to the APIs marked as unstable:

+
#[kani::unstable(feature="<IDENTIFIER>", issue="<TRACKING_ISSUE_NUMBER>", reason="<DESCRIPTION>")]
+pub fn unstable_api() {}
+
+

This is similar to the interface used by the standard library.

+

If the user tries to use an unstable feature in Kani without explicitly enabling it, +Kani will trigger an error. For unstable APIs, the error will be triggered during the crate +compilation.

+

Detailed Design

+

We will add the -Z option to both kani-driver and kani-compiler. +Kani driver will pass the information to the compiler.

+

For unstable APIs, the compiler will check if any reachable function uses an unstable feature that was not enabled. +If that is the case, the compiler will trigger a compilation error.

+

We will also change the compiler to only generate code for harnesses that match the harness filter. +The filter is already passed to the compiler, but it is currently only used for stubbing.

+

API Stabilization

+

Once an API has been stabilized, we will remove the unstable attributes from the given API. +If the user tries to enable a feature that was already stabilized, +Kani will print a warning stating that the feature has been stabilized.

+

API Removal

+

If we decide to remove an API that is marked as unstable, we should follow a regular deprecation +path (using #[deprecated] attribute), and keep the unstable flag + attributes, until we are +ready to remove the feature completely.

+

Rational and Alternatives

+

For this RFC, the suggestion is to only enable experimental features globally for simplicity of use and implementation.

+

For now, we will trigger a compilation error if an unstable API is reachable from a user crate +unless if the user opts in for the unstable feature.

+

We could allow users to specify experimental features on a per-harness basis, +but it could be tricky to make it clear to the user which harness may be affected by which feature. +The extra granularity would also be painful when we decide a feature is no longer experimental, +whether it is stabilized or removed. +In those cases, users would have to edit each harness that enables the affected feature.

+

Open questions

+
    +
  • Should we also add a stable attribute that documents when an API was stabilized?
  • +
+

Future possibilities

+
    +
  • Delay the error due to the usage of a unstable API, and only fail at runtime if the API is reachable.
  • +
  • Allow users to enable unstable features on a per-harness basis.
  • +
+
+
+

Summary

+

A new section in Kani's output to summarize the status of properties that depend on other properties. We use the term global conditions to refer to such properties.

+

User Impact

+

The addition of new options that affect the overall verification result depending on certain property attributes demands some consideration. +In particular, the addition of a new option to fail verification if there are uncoverable (i.e., unsatisfiable or unreachable) cover properties (requested in #2299) is posing new challenges to our current architecture and UI.

+

This concept isn't made explicit in Kani, but exists in some ways. +For example, the kani::should_panic attribute is a global condition because it can be described in terms of other properties (checks). +The request in #2299 is essentially another global conditions, and we may expect more to be requested in the future.

+

In this RFC, we propose a new section in Kani's output focused on reporting global conditions. +The goal is for users to receive useful information about hyperproperties without it becoming overwhelming. +This will help users to understand better options that are enabled through global conditions and ease the addition of such options to Kani.

+

User Experience

+

The output will refer to properties that depend on other properties as "global conditions", which is a simpler term. +The options to enable different global conditions will depend on a case-by-case basis1.

+

The main UI change in this proposal is a new GLOBAL CONDITIONS section that won't be printed if no global conditions have been enabled. +This section will only appear in Kani's default output after the RESULTS section (used for individual checks) and have the format:

+
GLOBAL CONDITIONS:
+ - `<name>`: <status> (<reason>)
+ - `<name>`: <status> (<reason>)
+ [...]
+
+

where:

+
    +
  • <name> is the name given to the global condition.
  • +
  • <status> is the status determined for the global condition.
  • +
  • <reason> is an explanation that depends on the status of the global condition.
  • +
+

For example, let's assume we implement the option requested in #2299. +A concrete example of this output would be:

+
GLOBAL CONDITIONS:
+ - `fail_uncoverable`: SUCCESS (all cover statements were satisfied as expected)
+
+

A FAILED status in any enabled global condition will cause verification to fail. +In that case, the overall verification result will point out that one or more global conditions failed, as in:

+
VERIFICATION:- FAILURE (one or more global conditions failed)
+
+

This last UI change will also be implemented for the terse output. +Finally, checks that cause an enabled global condition to fail will be reported using the same interface we use for failed checks2.

+

Global conditions which aren't enabled won't appear in the GLOBAL CONDITIONS section. +Their status will be computed regardless3, and we may consider showing this status when the --verbose option is passed.

+

The documentation of global conditions will depend on how they're enabled, which depends on a case-by-case basis. +However, we may consider adding a new subsection Global conditions to the Reference section that collects all of them so it's easier for users to consult all of them in one place.

+

Detailed Design

+

The only component to be modified is kani-driver since that's where verification results are built and determined. +But we should consider moving this logic into another crate.

+

We don't need new dependencies. +The corner cases will depend on the specific global conditions to be implemented.

+

Rationale and alternatives

+

As mentioned earlier, we're proposing this change to help users understand global conditions and how they're determined. +In many cases, global conditions empower users to write harnesses which weren't possible to write before. +As an example, the #[kani::should_panic] attribute allowed users to write harnesses expecting panic-related failures.

+

Also, we don't really know if more global conditions will be requested in the future. +We may consider discarding this proposal and waiting for the next feature that can be implemented as a global condition to be requested.

+

Alternative: Global conditions as regular checks

+

One option we've considered in the past is to enable global conditions as a regular checks. +While it's technically doable, it doesn't feel appropriate for global conditions to reported through regular checks since generally a higher degree of visibility may be appreciated.

+

Open questions

+

No open questions.

+

Future possibilities

+

A redesign of Kani's output is likely to change the style/architecture to report global conditions.

+
3 +

The results for global conditions would be computed during postprocessing based on the results of other checks. +Global conditions' checks aren't part of the SAT, therefore this computation won't impact verification time.

+
+
2 +

We do not discuss the specific interface to report the failed checks because it needs improvements (for both global conditions and standard verification). +In particular, the description field is the only information printed for properties (such as cover statements) without trace locations. +There are additional improvements we should consider: printing the actual status (for global conditions, this won't always be FAILED), avoid the repetition of Failed Checks: , etc. +This comment discusses problems with the current interface on some examples.

+
+
1 +

In other words, global conditions won't force a specific mechanism to be enabled. +For example, if the #[kani::should_panic] attribute is converted into a global condition, it will continue to be enabled through the attribute itself. +Other global conditions may be enabled through CLI flags only (e.g., --fail-uncoverable), or a combination of options in general.

+
+
+
+

Summary

+

Add verification-based line coverage reports to Kani.

+

User Impact

+

Nowadays, users can't easily obtain verification-based coverage reports in Kani. +Generally speaking, coverage reports show which parts of the code under verification are covered and which are not. +Because of that, coverage is often seen as a great metric to determine the quality of a verification effort.

+

Moreover, some users prefer using coverage information for harness development and debugging. +That's because coverage information provides users with more familiar way to interpret verification results.

+

This RFC proposes adding a new option for verification-based line coverage reports to Kani. +As mentioned earlier, we expect users to employ this coverage-related option on several stages of a verification effort:

+
    +
  • Learning: New users are more familiar with coverage reports than property-based results.
  • +
  • Development: Some users prefer coverage results to property-based results since they are easier to interpret.
  • +
  • CI Integration: Users may want to enforce a minimum percentage of code coverage for new contributions.
  • +
  • Debugging: Users may find coverage reports particularly helpful when inputs are over-constrained (missing some corner cases).
  • +
  • Evaluation: Users can easily evaluate where and when more verification work is needed (some projects aim for 100% coverage).
  • +
+

Moreover, adding this option directly to Kani, instead of relying on another tools, is likely to:

+
    +
  1. Increase the speed of development
  2. +
  3. Improve testing for coverage features
  4. +
+

Which translates into faster and more reliable coverage options for users.

+

User Experience

+

The goal is for Kani to generate code coverage report per harness in a well established format, such as LCOV, and possibly a summary in the output. +For now, we will focus on an interim solution that will enable us to assess the results of our instrumentation and enable integration with the Kani VS Code extension.

+

High-level changes

+

For the first version, this experimental feature will report verification results along coverage reports. +Because of that, we'll add a new section Coverage results that shows coverage results for each individual harness.

+

In the following, we describe an experimental output format. +Note that the final output format and overall UX is to be determined.

+

Experimental output format for coverage results

+

The Coverage results section for each harness will produce coverage information in a CSV format as follows:

+
<file>, <line>, <status>
+
+

where <status> is either FULL, PARTIAL or NONE.

+

As mentioned, this format is designed for evaluating the native instrumentation-based design and is likely to be substituted with another well-established format as soon as possible.

+

Users are not expected to consume this output directly. +Instead, coverage data is to be consumed by the Kani VS Code extension and displayed as in the VS Code Extension prototype.

+

How to activate and display coverage information in the extension is out of scope for this RFC. +That said, a proof-of-concept implementation is available here.

+

Detailed Design

+

Architecture

+

We will add a new unstable --coverage verification option to Kani which will require -Z line-coverage until this feature is stabilized. +We will also add a new --coverage-checks option to kani-compiler, which will result in the injection of coverage checks before each Rust statement and terminator1. +This option will be supplied by kani-driver when the --coverage option is selected. +These options will cause Kani to inject coverage checks during compilation and postprocess them to produce the coverage results sections described earlier.

+

Coverage Checks

+

Coverage checks are a new class of checks similar to cover checks. +The main difference is that users cannot directly interact with coverage checks (i.e., they cannot add or remove them manually). +Coverage checks are encoded as an assert(false) statement (to test reachability) with a fixed description. +In addition, coverage checks are:

+
    +
  • Hidden from verification results.
  • +
  • Postprocessed to produce coverage results.
  • +
+

In the following, we describe the injection and postprocessing procedures to generate coverage results.

+

Injection of Coverage Checks

+

The injection of coverage checks will be done while generating code for basic blocks. +This allows us to add one coverage check before each statement and terminator, which provides the most accurate results1. +It's not completely clear how this compares to the coverage instrumentation done in the Rust compiler, but an exploration to use the compiler APIs revealed that they're quite similar2.

+

Postprocessing Coverage Checks

+

The injection of coverage checks often results in one or more checks per line (assuming a well-formatted program). +We'll postprocess these checks so for each line

+
    +
  • if all checks are SATISFIED: return FULL
  • +
  • if all checks are UNSATISFIED: return NONE
  • +
  • otherwise: return PARTIAL
  • +
+

We won't report coverage status for lines which don't include a coverage check.

+

Rationale and alternatives

+

Benefits from a native coverage solution

+

Kani has relied on cbmc-viewer to report coverage information since the beginning. +In essence, cbmc-viewer consumes data from coverage-focused invocations of CBMC and produces an HTML report containing (1) coverage information and (2) counterexample traces. +Recently, there have been some issues with the coverage information reported by cbmc-viewer (e.g., #2048 or #1707), forcing us to mark the --visualize option as unstable and disable coverage results in the reports (in #2206).

+

However, it's possible for Kani to report coverage information without cbmc-viewer, as explained before. +This would give Kani control on both ends:

+
    +
  • The instrumentation performed on the program. Eventually, this would allow us to report more precise coverage information (maybe similar to Rust's instrument-based code coverage).
  • +
  • The format of the coverage report to be generated. Similarly, this would allow us to generate coverage data in different formats (see #1706 for GCOV, or #1777 for LCOV). While technically this is also doable from cbmc-viewer's output, development is likely to be faster this way.
  • +
+

Coverage through cbmc-viewer

+

As an alternative, we could fix and use cbmc-viewer to report line coverage.

+

Most of the issues with cbmc-viewer are generally due to:

+
    +
  1. Missing locations due to non-propagation of locations in either Kani or CBMC.
  2. +
  3. Differences in the definition of a basic block in CBMC and Rust's MIR.
  4. +
  5. Scarce documentation for coverage-related options (i.e., --cover <option>) in CBMC.
  6. +
  7. Limited testing with Rust code in cbmc-viewer.
  8. +
+

Note that (1) is not exclusive to coverage results from cbmc-viewer. +Finding checks with missing locations and propagating them if possible (as suggested in this comment) should be done regardless of the approach used for line coverage reports.

+

In contrast, (2) and (3) can be considered the main problems for Kani contributors to develop coverage options on top of cbmc-viewer and CBMC. +It's not clear how much effort this would involve, but (3) is likely to require substantial documentation contributions. +But (4) shouldn't be an issue if we decided to invest in cbmc-viewer.

+

Finally, the following downside must be considered: +cbmc-viewer can report line coverage but the path to report region-based coverage may involve a complete rewrite.

+

Other output formats

+

One of the long-term goals for this feature is to provide a UX that is familiar for users. +This is particularly relevant when talking about output formats. +Some services and frameworks working with certain coverage output formats have become quite popular.

+

However, this version doesn't consider common output formats (i.e., GCOV or LCOV) since coverage results will only be consumed by the Kani VS Code Extension at first. +But other output formats will be considered in the future.

+

Open questions

+

Open questions:

+
    +
  • Do we want to report line coverage as COVERED/UNCOVERED or FULL/PARTIAL/NONE?
  • +
  • Should we report coverage results and verification results or not? Doing both is likely to result in worse performance. We have to perform an experimental evaluation with hard benchmarks.
  • +
  • Should we instrument dependencies or not? Doing so is likely to result in worse performance. We have to perform an experimental evaluation.
  • +
  • What should be the final UX for this feature? For instance, we could print a coverage summary and generate a report file per harness. But it's not clear if individual results are relevant to users, so another possibility is to automatically combine results.
  • +
  • What's the most appropriate and well-established output format we can emit?
  • +
  • Determine if there are cases in which coverage information is confusing for users (due to, e.g., constant propagation or other compiler optimizations). How can work around such cases?
  • +
  • Do we want to report coverage information for dependencies? For CI, most users may be only interested in their code. Most coverage frameworks have an aggregation tool with an option to exclude dependencies from coverage metrics.
  • +
+

Feedback to gather before stabilization:

+ +

Future possibilities

+

We expect many incremental improvements in the coverage area:

+
    +
  1. Consuming the output produced in coverage results from the Kani VS Code extension.
  2. +
  3. Building a tool that produces coverage results by combining the coverage results of more than one harness.
  4. +
  5. Including span information in coverage checks and building region-based coverage reports.
  6. +
  7. Adding new user-requested coverage formats such as GCOV #1706 or LCOV #1777.
  8. +
+
1 +

We have experimented with different options for injecting coverage checks. +For example, we have tried injecting one before each basic block, or one before each statement, etc. +The proposed option (one before each statement AND each terminator) gives us the most accurate results.

+
+
2 +

In particular, comments in CoverageSpan and generate_coverage_spans hint that the initial set of spans come from Statements and Terminators. This comment goes in detail about the attempt to use the compiler APIs.

+
+
    +
  • Feature Name: Function Contracts
  • +
  • Feature Request Issue: #2652 and Milestone
  • +
  • RFC PR: #2620
  • +
  • Status: Unstable
  • +
  • Version: 1
  • +
  • Proof-of-concept: features/contracts
  • +
  • Feature Gate: -Zfunction-contracts, enforced by compile time error1
  • +
+
+

Summary

+

Function contracts are a means to specify and check function behavior. On top of +that the specification can then be used as a sound2 +abstraction to replace the concrete implementation, similar to stubbing.

+

This allows for a modular verification.

+ +

User Impact

+ +

Function contracts provide an interface for a verified, +sound2 function abstraction. This is similar to stubbing +but with verification of the abstraction instead of blind trust. This allows for +modular verification, which paves the way for the following two ambitious goals.

+
    +
  • Scalability: A function contract is an abstraction (sound +overapproximation) of a function's behavior. After verifying the contract +against its implementation we can subsequently use the (cheaper) abstraction +instead of the concrete implementation when analyzing its callers. +Verification is thus modularized and even cacheable.
  • +
  • Unbounded Verification: Contracts enable inductive reasoning for recursive +functions where the first call is checked against the contract and recursive +calls are stubbed out using the abstraction.
  • +
+

Function contracts are completely optional with no user impact if unused. This +RFC proposes the addition of new attributes, and functions, that shouldn't +interfere with existing functionalities.

+

User Experience

+

A function contract specifies the behavior of a function as a predicate that +can be checked against the function implementation and also used as an +abstraction of the implementation at the call sites.

+

The lifecycle of a contract is split into three phases: specification, +verification and call abstraction, which we will explore on this example:

+
fn my_div(dividend: u32, divisor: u32) -> u32 {
+  dividend / divisor
+}
+
+
    +
  1. +

    In the first phase we specify the contract. Kani provides two new +annotations: requires (preconditions) to describe the expectations this +function has as to the calling context and ensures (postconditions) which +approximates function outputs in terms of function inputs.

    +
    #[kani::requires(divisor != 0)]
    +#[kani::ensures(|result : &u32| *result <= dividend)]
    +fn my_div(dividend: u32, divisor: u32) -> u32 {
    +  dividend / divisor
    +}
    +
    +

    requires here indicates this function expects its divisor input to never +be 0, or it will not execute correctly (for instance panic or cause undefined +behavior).

    +

    ensures puts a bound on the output, relative to the dividend input.

    +

    Conditions in contracts are Rust expressions which reference the +function arguments and, in case of ensures, the return value of the +function. The return value is passed into the ensures closure statement by reference. Syntactically +Kani supports any Rust expression, including function calls, defining types +etc. However they must be side-effect free (see also side effects +here) or Kani will throw a compile error.

    +

    Multiple requires and ensures clauses are allowed on the same function, +they are implicitly logically conjoined.

    +
  2. +
  3. +

    Next, Kani ensures that the function implementation respects all the conditions specified in its contract.

    +

    To perform this check Kani needs a suitable harness to verify the function +in. The harness is mainly responsible for providing the function arguments +but also set up a valid heap that pointers may refer to and properly +initialize static variables.

    +

    Kani demands of us, as the user, to provide this harness; a limitation of +this proposal. See also future possibilities for a +discussion about the arising soundness issues and their remedies.

    +

    Harnesses for checking contract are defined with the +proof_for_contract(TARGET) attribute which references TARGET, the +function for which the contract is supposed to be checked.

    +
    #[kani::proof_for_contract(my_div)]
    +fn my_div_harness() {
    +  my_div(kani::any(), kani::any())
    +}
    +
    +

    Similar to a verification harness for any other function, we are supposed to +create all possible input combinations the function can encounter, then call +the function at least once with those abstract inputs. If we forget to call +my_div Kani reports an error. Unlike other harnesses we only need to create +suitable data structures but we don't need to add any checks as Kani will +use the conditions we specified in the contract.

    +

    Kani inserts preconditions (requires) as kani::assume before the call +to my_div, limiting inputs to those the function is actually defined for. +It inserts postconditions (ensures) as kani::assert checks after the +call to my_div, enforcing the contract.

    +

    The expanded version of our harness that Kani generates looks roughly like +this:

    +
    #[kani::proof]
    +fn my_div_harness() {
    +  let dividend = kani::any();
    +  let divisor = kani::any();
    +  kani::assume(divisor != 0); // requires
    +  let result_kani_internal = my_div(dividend, divisor);
    +  kani::assert((|result : &u32| *result <= dividend)(result_kani_internal)); // ensures
    +}
    +
    +

    Kani verifies the expanded harness like any other harness, giving the +green light for the next step: call abstraction.

    +
  4. +
  5. +

    In the last phase the verified contract is ready for us to use to +abstract the function at its call sites.

    +

    Kani requires that there has to be at least one associated +proof_for_contract harness for each abstracted function, otherwise an error is +thrown. In addition, by default, it requires all proof_for_contract +harnesses to pass verification before attempting verification of any +harnesses that use the contract as a stub.

    +

    A possible harness that uses our my_div contract could be the following:

    +
    #[kani::proof]
    +#[kani::stub_verified(my_div)]
    +fn use_div() {
    +  let v = vec![...];
    +  let some_idx = my_div(v.len() - 1, 3);
    +  v[some_idx];
    +}
    +
    +

    At a call site where the contract is used as an abstraction Kani +kani::asserts the preconditions (requires) and produces a +nondeterministic value (kani::any) which satisfies the postconditions.

    +

    Mutable memory is similarly made non-deterministic, discussed later in +havocking.

    +

    An expanded stubbing of my_div looks like this:

    +
    fn my_div_stub(dividend: u32, divisor: u32) -> u32 {
    +  kani::assert(divisor != 0); // pre-condition
    +  kani::any_where(|result| { /* post-condition */ result <= dividend })
    +}
    +
    +

    Notice that this performs no actual computation for my_div (other than the +conditions) which allows us to avoid something potentially costly.

    +
  6. +
+

Also notice that Kani was able to express both contract checking and abstracting +with existing capabilities; the important feature is the enforcement. The +checking is, by construction, performed against the same condition that is +later used as the abstraction, which ensures soundness (see discussion on +lingering threats to soundness in the future section) +and guarding against abstractions diverging from their checks.

+

Write Sets and Havocking

+

Functions can have side effects on data reachable through mutable references or +pointers. To overapproximate all such modifications a function could apply to +pointed-to data, the verifier "havocs" those regions, essentially replacing +their content with non-deterministic values.

+

Let us consider a simple example of a pop method.

+
impl<T> Vec<T> {
+  fn pop(&mut self) -> Option<T> {
+    ...
+  }
+}
+
+

This function can, in theory, modify any memory behind &mut self, so this is +what Kani will assume it does by default. It infers the "write set", that is the +set of memory locations a function may modify, from the type of the function +arguments. As a result, any data pointed to by a mutable reference or pointer is +considered part of the write set3. In addition, a static +analysis of the source code discovers any static mut variables the function or +it's dependencies reference and adds all pointed-to data to the write set also.

+

During havocking the verifier replaces all locations in the write set with +non-deterministic values. Kani emits a set of automatically generated +postconditions which encode the expectations from the Rust type system and +assumes them for the havocked locations to ensure they are valid. This +encompasses both limits as to what values are acceptable for a given type, such +as char or the possible values of an enum discriminator, as well as lifetime +constraints.

+

While the inferred write set is sound and enough for successful contract +checking4 in many cases this inference is too coarse +grained. In the case of pop every value in this vector will be made +non-deterministic.

+

To address this the proposal also adds a modifies and frees clause which +limits the scope of havocking. Both clauses represent an assertion that the +function will modify only the specified memory regions. Similar to +requires/ensures the verifier enforces the assertion in the checking stage to +ensure soundness. When the contract is used as an abstraction, the modifies +clause is used as the write set to havoc.

+

In our pop example the only modified memory location is the last element and +only if the vector was not already empty, which would be specified thusly.

+
impl<T> Vec<T> {
+  #[modifies(if !self.is_empty() => (*self).buf.ptr.pointer.pointer[self.len])]
+  #[modifies(if self.is_empty())]
+  fn pop(&mut self) -> Option<T> {
+    ...
+  }
+}
+
+

The #[modifies(when = CONDITION, targets = { MODIFIES_RANGE, ... })] consists +of a CONDITION and zero or more, comma separated MODIFIES_RANGEs which are +essentially a place expression.

+

Place expressions describe a position in the abstract program memory. You may +think of it as what goes to the left of an assignment. They compose of the name +of one function argument (or static variable) and zero or more projections +(dereference *, field access .x and slice indexing [1]5).

+

If no when is provided the condition defaults to true, meaning the modifies +ranges apply to all invocations of the function. If targets is omitted it +defaults to {}, e.g. an empty set of targets meaning under this condition the +function modifies no mutable memory.

+

Because place expressions are restricted to using projections only, Kani must +break Rusts pub/no-pub encapsulation here6. +If need be we can reference fields that are usually hidden, without an error +from the compiler.

+

In addition to a place expression, a MODIFIES_RANGE can also be terminated +with more complex slice expressions as the last projection. This only applies +to *mut pointers to arrays. For instance this is needed for Vec::truncate +where all of the latter section of the allocation is assigned (dropped).

+
impl<T> Vec<T> {
+  #[modifies(self.buf.ptr.pointer.pointer[len..])]
+  fn truncate(&mut self, len: usize) {
+    ...
+  }
+}
+
+

[..] denotes the entirety of an allocation, [i..], [..j] and [i..j] are +ranges of pointer offsets5. The slice indices are offsets with sizing T, e.g. +in Rust p[i..j] would be equivalent to +std::slice::from_raw_parts(p.offset(i), i - j). i must be smaller or equal +than j.

+

A #[frees(when = CONDITION, targets = { PLACE, ... })] clause works similarly +to modifies but denotes memory that is deallocated. Like modifies it applies +only to pointers but unlike modifies it does not admit slice syntax, only +place expressions, because the whole allocation has to be freed.

+

History Expressions

+

Kani's contract language contains additional support to reason about changes of +mutable memory. One case where this is necessary is whenever ensures needs to +refer to state before the function call. By default variables in the ensures +clause are interpreted in the post-call state whereas history expressions are +interpreted in the pre-call state.

+

Returning to our pop function from before we may wish to describe in which +case the result is Some. However that depends on whether self is empty +before pop is called. To do this Kani provides the old(EXPR) pseudo +function (see this section about a discussion on naming), +which evaluates EXPR before the call (e.g. to pop) and makes the result +available to ensures. It is used like so:

+
impl<T> Vec<T> {
+  #[kani::ensures(|result : &Option<T>| old(self.is_empty()) || result.is_some())]
+  fn pop(&mut self) -> Option<T> {
+    ...
+  }
+}
+
+

old allows evaluating any Rust expression in the pre-call context, so long as +it is free of side-effects. See also this +explanation. The borrow checker enforces that the +mutations performed by e.g. pop cannot be observed by the history expression, as +that would defeat the purpose. If you wish to return borrowed content from +old, make a copy instead (using e.g. clone()).

+

Note also that old is syntax, not a function and implemented as an extraction +and lifting during code generation. It can reference e.g. pop's arguments but +not local variables. Compare the following

+

Invalid ❌: #[kani::ensures(|result : &Option<T>| { let x = self.is_empty(); old(x) } || result.is_some())]
+Valid ✅: #[kani::ensures(|result : &Option<T>| old({ let x = self.is_empty(); x }) || result.is_some())]

+

And it will only be recognized as old(...), not as let old1 = old; old1(...) etc.

+

Workflow and Attribute Constraints Overview

+
    +
  1. By default kani or cargo kani first verifies all contract harnesses +(proof_for_contract) reachable from the file or in the local workspace +respectively.
  2. +
  3. Each contract (from the local +crate7) that is used in a +stub_verified is required to have at least one associated contract harness. +Kani reports any missing contract harnesses as errors.
  4. +
  5. Kani verifies all regular harnesses if their stub_verified contracts +passed step 1 and 2.
  6. +
+

When specific harnesses are selected (with --harness) contracts are not +verified.

+

Kani reports a compile time error if any of the following constraints are violated:

+
    +
  • +

    A function may have any number of requires, ensures, modifies and frees +attributes. Any function with at least one such annotation is considered as +"having a contract".

    +

    Harnesses (general or for contract checking) may not have any such annotation.

    +
  • +
  • +

    A harness may have up to one proof_for_contract(TARGET) annotation where TARGET must +"have a contract". One or more proof_for_contract harnesses may have the +same TARGET.

    +

    A proof_for_contract harness may use any harness attributes, including +stub and stub_verified, though the TARGET may not appear in either.

    +
  • +
  • +

    Kani checks that TARGET is reachable from the proof_for_contract harness, +but it does not warn if abstracted functions use TARGET8.

    +
  • +
  • +

    A proof_for_contract function may not have the kani::proof attribute (it +is already implied by proof_for_contract).

    +
  • +
  • +

    A harness may have multiple stub_verified(TARGET) attributes. Each TARGET +must "have a contract". No TARGET may appear twice. Each local TARGET is +expected to have at least one associated proof_for_contract harness which +passes verification, see also the discussion on when to check contracts in +open questions.

    +
  • +
  • +

    Harnesses may combine stub(S_TARGET, ..) and stub_verified(V_TARGET) +annotations, though no target may occur in S_TARGETs and V_TARGETs +simultaneously.

    +
  • +
  • +

    For mutually recursive functions using stub_verified, Kani will check their +contracts in non-deterministic order and assume each time the respective other +check succeeded.

    +
  • +
+

Detailed Design

+ +

Kani implements the functionality of function contracts in three places.

+
    +
  1. Code generation in the requires and ensures macros (kani_macros).
  2. +
  3. GOTO level contracts using CBMC's contract language generated in +kani-compiler for modifies clauses.
  4. +
  5. Dependencies and ordering among harnesses in kani-driver to enforce +contract checking before replacement. Also plumbing between compiler and +driver for enforcement of assigns clauses.
  6. +
+

Code generation in kani_macros

+

The requires and ensures macros perform code generation in the macro, +creating a check and a replace function which use assert and assume as +described in the user experience section. Both are attached +to the function they are checking/replacing by kanitool::checked_with and +kanitool::replaced_with attributes respectively. See also the +discussion about why we decided to generate check +and replace functions like this.

+

The code generation in the macros is straightforward, save two aspects: old +and the borrow checker.

+

The special old builtin function is implemented as an AST rewrite. Consider +the below example:

+
impl<T> Vec<T> {
+  #[kani::ensures(|result : &Option<T>| self.is_empty() || self.len() == old(self.len()) - 1)]
+  fn pop(&mut self) -> Option<T> {
+    ...
+  }
+}
+
+

The ensures macro performs an AST rewrite consisting of an extraction of the +expressions in old and a replacement with a fresh local variable, creating the +following:

+
impl<T> Vec<T> {
+  fn check_pop(&mut self) -> Option<T> {
+    let old_1 = self.len();
+    let result_kani_internal = Self::pop(self);
+    kani::assert((|result : &Option<T>| self.is_empty() || self.len() == old_1 - 1)(result_kani_internal))
+  }
+}
+
+

Nested invocations of old are prohibited (Kani throws an error) and the +expression inside may only refer to the function arguments and not other local +variables in the contract (Rust will report those variables as not being in +scope).

+

The borrow checker also ensures for us that none of the temporary variables +borrow in a way that would be able to observe the modification in pop which +would occur for instance if the user wrote old(self). Instead of borrowing +copies should be created (e.g. old(self.clone())). This is only enforced for +safe Rust though.

+

The second part relevant for the implementation is how we deal with the borrow +checker for postconditions. They reference the arguments of the function after +the call which is problematic if part of an input is borrowed mutably in the +return value. For instance the Vec::split_at_mut function does this and a +sensible contract for it might look as follows:

+
impl<T> Vec<T> {
+  #[ensures(|result : &(&mut [T], &mut [T])| self.len() == result.0.len() + result.1.len())]
+  fn split_at_mut(&mut self, i: usize) -> (&mut [T], &mut [T]) {
+    ...
+  }
+}
+
+

This contract refers simultaneously to self and the result. Since the method +however borrows self mutably, it would no longer be accessible in the +postcondition. To work around this we strategically break the borrowing rules +using a new hidden builtin kani::unchecked_deref with the type signature for <T> fn (&T) -> T which is essentially a C-style dereference operation. Breaking +the borrow checker like this is safe for 2 reasons:

+
    +
  1. Postconditions are not allowed perform mutation and
  2. +
  3. Post conditions are of type bool, meaning they cannot leak references to +the arguments and cause the race conditions the Rust type system tries to +prevent.
  4. +
+

The "copies" of arguments created by unsafe_deref are stored as fresh local +variables and their occurrence in the postcondition is renamed. In addition a +mem::forget is emitted for each copy to avoid a double free.

+

Recursion

+

Kani verifies contracts for recursive functions inductively. Reentry of the +function is detected with a function-specific static variable. Upon detecting +reentry we use the replacement of the contract instead of the function body.

+

Kani generates an additional wrapper around the function to add the detection. +The additional wrapper is there so we can place the modifies contract on +check_pop and replace_pop instead of recursion_wrapper which prevents CBMC +from triggering its recursion induction as this would skip our replacement checks.

+
#[checked_with = "recursion_wrapper"]
+#[replaced_with = "replace_pop"]
+fn pop(&mut self) { ... }
+
+fn check_pop(&mut self) { ... }
+
+fn replace_pop(&mut self) { ... }
+
+fn recursion_wrapper(&mut self) { 
+  static mut IS_ENTERED: bool = false;
+
+  if unsafe { IS_ENTERED } {
+    replace_pop(self)
+  } else {
+    unsafe { IS_ENTERED = true; }
+    let result = check_pop(self);
+    unsafe { IS_ENTERED = false; }
+    result
+  };
+}
+
+

Note that this is insufficient to verify all types of recursive functions, as +the contract specification language has no support for inductive lemmas (for +instance in ACSL section 2.6.3 +"inductive predicates"). Inductive lemmas are usually needed for recursive +data structures.

+

Changes to Other Components

+

Contract enforcement and replacement (kani::proof_for_contract(f), +kani::stub_verified(f)) both dispatch to the stubbing logic, stubbing f +with the generated check and replace function respectively. If f has no +contract, Kani throws an error.

+

For write sets Kani relies on CBMC. modifies clauses (whether derived from +types or from explicit clauses) are emitted from the compiler as GOTO contracts +in the artifact. Then the driver invokes goto-instrument with the name of the +GOTO-level function names to enforce or replace the memory contracts. The +compiler communicates the names of the function via harness metadata.

+

Code used in contracts is required to be side effect free which means it +must not perform I/O, mutate memory (&mut vars and such) or (de)allocate heap +memory. This is enforced in two layers. First with an MIR traversal over all +code reachable from a contract expression. An error is thrown if known +side-effecting actions are performed such as ptr::write, malloc, free or +functions which we cannot check, such as e.g. extern "C", with the exception +of known side effect free functions in e.g. the standard library.

+ +

Rationale and alternatives

+ + +

We developed the old contract for history expressions via understanding it as a modality originating from Moggi 1991. +The old monad links the "language of the past" to the "language of the present". +Implementing the full generality of the monad is rather difficult, so we focus on a particular usage of the monad.

+

We have an external syntax representation which is what the user inputs. We then parse this and logically manipulate it as a monad, prefixing all the bind operations. We then output the final compiled macro output as Rust code.

+

In particular, if we have an ensures statement like

+
#[kani::ensures(old(*ptr)+1==*ptr)]
+
+

Then we comprehend this as syntax for the statement (not within Rust)

+
bind (*ptr : O(u32)) (|remember : u32| remember + 1 == *ptr)
+
+

Here, the O(u32) is taking the type of the past u32 and converting it into a type in the present O(u32) while the bind operation lets you use the value of the past u32 to express a type in the present bool.

+

This then gets compiled to (within Rust)

+
let remember = *ptr;
+let result = ...;
+kani::assert(remember + 1 == *ptr)
+
+

This means that the underlying principle of the monad is there, but external syntax appears to be less like a monad because otherwise it would be too difficult to implement, and the user most likely only cares about this particular construction of prefixing all the bind operations.

+

This construction requires that old expressions are closed with resprect to the input parameters. This is due to the lifting into the prefixed bind operations.

+

A major drawback is that eta expansion fails. If we eta expand a function f, it becomes |x|f(x). Note that eta expansions guarantee that the original f and the |x|f(x) are equivalent which makes a lot of sense since you’re just calling the same function. However, we have that old(y) is not equivalent to (|x|old(x))(y). y is a closed expression, so the first statement works. x is a bound variable, so it is an open expression, so compilation will fail.

+

The reason for this restriction is that the user will most likely only want to use this particular prefixed bind structure for their code, so exposing the concept of monads to the user level would only confuse the user. It is also simpler from an implementation perspective to limit the monad to this particular usage.

+

As for nested old, such as old(old(*ptr)+*ptr), it is reasonable to interpret this as syntax representing

+
bind (bind(*ptr)(|remember_1| remember_1 + *ptr)) (|remember_0| ...)
+
+

which compiles to

+
let remember_1 = *ptr;
+let remember_0 = remember_1 + *ptr;
+let result = ...;
+...
+
+

so the restriction is just a matter of there not being implementation support for this kind of statement rather than the theory itself. It is not particularly useful to implement this because we claim that there should be no effectful computation within the contracts, so you can substitute the remember_1 into the second line without worrying about the effects. Hence, we opt for simply restricting this behavior instead of implementing it. (Note: it can be implemented by changing denier.visit_expr_mut(e); into self.visit_expr_mut(e);)

+ +

Kani-side implementation vs CBMC

+

Instead of generating check and replace functions in Kani, we could use the contract instrumentation provided by CBMC.

+

We tried this earlier but came up short, because it is difficult to implement, +while supporting arbitrary Rust syntax. We exported the conditions into +functions so that Rust would do the parsing/type checking/lowering for us and +then call the lowered function in the CBMC contract.

+

The trouble is that CBMC's old is only supported directly in the contract, not +in functions called from the contract. This means we either need to inline the +contract function body, which is brittle in the presence of control flow, or we +must extract the old expressions, evaluate them in the contract directly and +pass the results to the check function. However this means we must restrict the +expressions in old, because we now need to lower those by hand and even if we +could let rustc do it, CBMC's old has no support for function calls in its +argument expression.

+

Expanding all contract macros at the same time

+

Instead of expanding contract macros one-at-a-time and layering the checks we +could expand all subsequent one's with the outermost one in one go.

+

This is however brittle with respect to renaming. If a user does use kani::requires as my_requires and then does multiple +#[my_requires(condition)] macro would not collect them properly since it can +only match syntactically and it does not know about the use and neither can we +restrict this kind of use or warn the user. By contrast, the collection with +kanitool::checked_with is safe, because that attribute is generated by our +macro itself, so we can rely on the fact that it uses the canonical +representation.

+

Generating nested functions instead of siblings

+

Instead of generating the check and replace functions as siblings to the +contracted function we could nest them like so

+
fn my_div(dividend: u32, divisor: u32) -> u32 {
+  fn my_div_check_5e3713(dividend: u32, divisor: u32) -> u32 {
+    ...
+  }
+  ...
+}
+
+

This could be beneficial if we want to be able to allow contracts on trait impl +items, in which case generating sibling functions is not allowed. On the other +hand this makes it harder to implement contracts on trait definitions, +because there is no body available which we could nest the function into. +Ultimately we may require both so that we can support both.

+

What is required to make this work is an additional pass over the condition that +replaces every self with a fresh identifier that now becomes the first +argument of the function. In addition there are open questions as to how to +resolve the nested name inside the compiler.

+

Explicit command line checking/substitution vs attributes:

+

Instead of +adding a new special proof_for_contact attributes we could have instead done:

+
    +
  1. Check contracts on the command line like CBMC does. This makes contract +checking a separate kani invocation with something like a +--check-contract flag that directs the system to instrument the function. +This is a very flexible design, but also easily used incorrectly. +Specifically nothing in the source indicates which harnesses are supposed +to be used for which contract, users must remember to invoke the check and +are also responsible for ensuring they really do verify all contacts they +will later be replacing and lastly.
  2. +
  3. Check contracts with a #[kani::proof] harness. This would have used +e.g. a #[kani::for_contract] attributes on a #[kani::proof]. Since +#[kani::for_contract] is only valid on a proof, we decided to just +imply it and save the user some headache. Contract checking harnesses are +not meant to be reused for other purposes anyway and if the user really +wants to the can just factor out the actual contents of the harness to +reuse it.
  4. +
+

Polymorphism during contract checking

+

A current limitation with how contracts are enforced means that if the target of +a proof_for_contract is polymorphic, only one monomorphization is permitted to +occur in the harness. This does not limit the target to a single occurrence, +but to a single instantiation of its generic parameters.

+

This is because we rely on CBMC for enforcing the modifies contract. At the +GOTO level all monomorphized instances are distinct functions and CBMC only +allows checking one function contract at a time, hence this restriction.

+

User supplied harnesses

+

We make the user supply the harnesses for checking contracts. This is our major +source of unsoundness, if corner cases are not adequately covered. Having Kani +generate the harnesses automatically is a non-trivial task (because heaps are +hard) and will be the subject of future improvements.

+

In limited cases we could generate harnesses, for instance if only bounded types +(integers, booleans, enums, tuples, structs, references and their combinations) +were used. We could restrict the use of contracts to cases where only such types +are involved in the function inputs and outputs, however this would drastically +limit the applicability, as even simple heap data structures such as Vec, +String and even &[T] and &str (slices) would be out of scope. These data +structures however are ubiquitous and users can avoid the unsoundness with +relative confidence by overprovisioning (generating inputs that are several +times larger than what they expect the function will touch).

+

Open questions

+ +
    +
  • +

    Returning kani::any() in a replacement isn't great, because it wouldn't work +for references as they can't have an Arbitrary implementation. Plus the +soundness then relies on a correct implementation of Arbitrary. Instead it +may be better to allow for the user to specify type invariants which can the +be used to generate correct values in replacement but also be checked as part +of the contract checking.

    +
  • +
  • +

    Making result special. Should we use special syntax here like @result or +kani::result(), though with the latter I worry that people may get confused +because it is syntactic and not subject to usual use renaming and import +semantics. Alternatively we can let the user pick the name with an additional +argument to ensures, e.g. ensures(my_result_var, CONDITION)

    +

    Similar concerns apply to old, which may be more appropriate to be special +syntax, e.g. @old.

    +

    See #2597

    +
  • +
  • +

    How to check the right contracts at the right time. By default kani and +cargo kani check all contracts in a crate/workspace. This represents the +safest option for the user but may be too costly in some cases.

    +

    The user should be provided with options to disable contract checking for the +sake of efficiency. Such options may look like this:

    +
      +
    • By default (kani/cargo kani) all local contracts are checked, +harnesses are only checked if the contracts they depend on succeeded their check.
    • +
    • With harness selection (--harness) only those contracts which the +selected harnesses depend on are checked.
    • +
    • For high assurance passing a --paranoid flag also checks contracts for +dependencies (other crates) when they are used in abstractions.
    • +
    • Per harness the users can disable the checking for specific contracts +via attribute, like #[stub_verified(TARGET, trusted)] or +#[stub_unverified(TARGET)]. This also plays nicely with cfg_attr.
    • +
    • On the command line users can similarly disable contract checks by +passing (multiple times) --trusted TARGET to skip checking those +contracts.
    • +
    • The bold (or naïve) user can skip all contracts with --all-trusted.
    • +
    • For the lawyer that is only interested in checking contracts and nothing +else a --litigate flag checks only contract harnesses.
    • +
    +

    Aside: I'm obviously having some fun here with the names, happy to change, +it's really just about the semantics.

    +
  • +
  • +

    Can old accidentally break scope? The old function cannot reference local +variables. For instance #[ensures({let x = ...; old(x)})] cannot work as an +AST rewrite because the expression in old is lifted out of it's context into +one where the only bound variables are the function arguments (see also +history expressions). In most cases this will be a +compiler error complaining that x is unbound, however it is possible that +if there is also a function argument x, then it may silently succeed the +code generation but confusingly fail verification. For instance #[ensures({ let x = 1; old(x) == x })] on a function that has an argument named x would +not hold.

    +

    To handle this correctly we would need an extra check that detects if old +references local variables. That would also enable us to provide a better +error message than the default "cannot find value x in this scope".

    +
  • +
  • +

    Can panicking be expected behavior? Usually preconditions are used to rule +out panics but it is conceivable that a user would want to specify that a +function panics under certain conditions. Specifying this would require an +extension to the current interface.

    +
  • +
  • +

    UB checking. With unsafe rust it is possible to break the type system +guarantees in Rust without causing immediate errors. Contracts must be +cognizant of this and enforce the guarantees as part of the contract or +require users to explicitly defer such checks to use sites. The latter case +requires dedicated support because the potential UB must be reflected in the +havoc.

    +
  • +
  • +

    modifies clauses over patterns. Modifies clauses mention values bound in +the function header and as a user I would expect that if I use a pattern in +the function header then I can use the names bound in that pattern as base +variables in the modifies clause. However modifies clauses are implemented +as assigns clauses in CBMC which does not have a notion of function header +patterns. Thus it is necessary to project any modifies ranges deeper by the +fields used in the matched pattern.

    +
  • +
+ +

Future possibilities

+ +
    +
  • +

    Quantifiers: Quantifiers are like logic-level loops and a powerful +reasoning helper. CBMC has support for both exists and forall, but the +code generation is difficult. The most ergonomic and easy way to implement +quantifiers on the Rust side is as higher-order functions taking Fn(T) -> bool, where T is some arbitrary type that can be quantified over. This +interface is familiar to developers, but the code generation is tricky, as +CBMC level quantifiers only allow certain kinds of expressions. This +necessitates a rewrite of the Fn closure to a compliant expression.

    +
  • +
  • +

    Letting the user supply the harnesses for checking contracts is a source of +unsoundness, if corner cases are not adequately covered. Ideally Kani would +generate the check harness automatically, but this is difficult both because +heap data structures are potentially infinite, and also because it must observe +user-level type invariants.

    +

    A complete solution for this is not known to us but there are ongoing +investigations into harness generation mechanisms in CBMC.

    +

    Function inputs that are non-inductive could be created from the type as the +safe Rust type constraints describe a finite space.

    +

    For dealing with pointers one applicable mechanism could be memory +predicates to declaratively describe the state of the heap both before and +after the function call.

    +

    In CBMC's implementation memory predicates are part of the pre/postconditions. +This does not easily translate to Kani, since we handle pre/postconditions +manually and mainly in proc-macros. There are multiple ways to bridge this +gap, perhaps the easiest being to add memory predicates separately to Kani +instead of as part of pre/postconditions, so they can be handled by forwarding +them to CBMC. However this is also tricky, because memory predicates are used +to describe pointers and pointers only. Meaning that if they are encapsulated +in a structure (such as Vec or RefCell) there is no way of specifying the +target of the predicate without breaking encapsulation (similar to +modifies). In addition there are limitations also on the pointer predicates +in CBMC itself. For instance they cannot be combined with quantifiers.

    +

    A better solution would be for the data structure to declare its own +invariants at definition site which are automatically swapped in on every +contract that uses this type.

    +
  • +
  • +

    What about mutable trait inputs (wrt memory access patters), e.g. a mut impl AccessMe

    +
  • +
  • +

    Trait contracts: Our proposal could be extended easily to handle simple +trait contracts. The macros would generate new trait methods with default +implementations, similar to the functions it generates today. Using sealed +types we can prevent the user from overwriting the generated contract methods. +Contracts for the trait and contracts on it's impls are combined by abstracting +the original method depending on context. The occurrence inside the contract +generated from the trait method is replaced by the impl contract. Any other +occurrence is replaced by the just altered trait method contract.

    +
  • +
  • +

    Cross Session Verification Caching: This proposal focuses on scalability +benefits within a single verification session, but those verification results +could be cached across sessions and speed up verification for large projects +using contacts in the future.

    +
  • +
  • +

    Inductive Reasoning: Describing recursive functions can require that the +contract also recurse, describing a fixpoint logic. This is needed for +instance for linked data structures like linked lists or trees. Consider for +instance a reachability predicate for a linked list:

    +
    struct LL<T> { head: T, next: *const LL<T> }
    +
    +fn reachable(list: &LL<T>, t: &T) -> bool {
    +    list.head == t
    +    || unsafe { next.as_ref() }.map_or(false, |p| reachable(p, t))
    +}
    +
    +
    +
  • +
  • +

    Compositional Contracts: The proposal in this document lacks a +comprehensive handling of type parameters. Contract checking harnesses require +monomorphization. However this means the contract is only checked against a +finite number of instantiations of any type parameter (at most as many as +contract checking harnesses were defined). There is nothing preventing the +user from using different instantiations of the function's type parameters.

    +

    A function (f()) can only interact with its type parameters P through the +traits (T) they are constrained over. We can require T to carry contracts +on each method T::m(). During checking we can use a synthetic type that +abstracts T::m() with its contract. This way we check f() against Ts +contract. Then we later abstract f() we can ensure any instantiations of P +have passed verification of the contract of T::m(). This makes the +substitution safe even if the particular type has not been used in a checking +harness.

    +

    For higher order functions this gets a bit more tricky, as closures are ad-hoc +defined types. Here the contract for the closure could be attached to f() +and then checked for each closure that may be provided. However this does not +work so long as the user has to provide the harnesses, as they cannot recreate +the closure type.

    +
  • +
+
+
1 +

Enforced gates means all uses of constructs (functions, annotations, +macros) in this RFC are an error.

+
+
2 +

The main remaining threat to soundness in the use of +contracts, as defined in this proposal, is the reliance on user-supplied +harnesses for contract checking (explained in item 2 of user +experience). A more thorough discussion on the dangers +and potential remedies can be found in the future +section.

+
+
3 +

For inductively defined types the write set inference +will only add the first "layer" to the write set. If you wish to modify +deeper layers of a recursive type an explicit modifies clause is required.

+
+
4 +

While inferred memory footprints are sound for both safe +and unsafe Rust certain features in unsafe rust (e.g. RefCell) get +inferred incorrectly and will lead to a failing contract check.

+
+
5 +

Slice indices can be place expressions referencing function +arguments, constants and integer arithmetic expressions. Take for example +this Vec method (places simplified vs. actual implementation in std): +fn truncate(&mut self, len: usize). A relatively precise contract for this +method can be achieved with slice indices like so: +#[modifies(self.buf[len..self.len], self.len)]

+
+
6 +

Breaking the pub encapsulation has +unfortunate side effects because it means the contract depends on non-public +elements which are not expected to be stable and can drastically change even +in minor versions. For instance if your project depends on crate a which +in turn depends on crate b, and a::foo has a contract that takes as +input a pointer data structure b::Bar then a::foos assigns contract +must reference internal fields of b::Bar. Say your project depends on the +replacement of a::foo, if b changes the internal representation of +Bar in a minor version update cargo could bump your version of b, +breaking the contract of a::foo (it now crashes because it e.g. references +non-existent fields). +You cannot easily update the contract for a::foo, since it is a +third-party crate; in fact even the author of a could not properly update +to the new contract since their old version specification would still admit +the new, broken version of b. They would have to yank the old version and +explicitly nail down the exact minor version of b which defeats the whole +purpose of semantic versioning.

+
+
7 +

Contracts for functions from +external crates (crates from outside the workspace, which is not quite the +definition of extern crate in Rust) are not checked by default. The +expectation is that the library author providing the contract has performed +this check. See also open question for a discussion on +defaults and checking external contracts.

+
+
8 +

Kani cannot report the occurrence of a contract function to check +in abstracted functions as errors, because the mechanism is needed to verify +mutually recursive functions.

+
+
    +
  • Feature Name: Quantifiers
  • +
  • Feature Request Issue: #2546 and #836
  • +
  • RFC PR: #
  • +
  • Status: Unstable
  • +
  • Version: 1.0
  • +
+
+

Summary

+

Quantifiers are logical operators that allow users to express that a property or condition applies to some or all objects within a given domain.

+

User Impact

+

There are two primary quantifiers: the existential quantifier (∃) and the universal quantifier (∀).

+
    +
  1. +

    The existential quantifier (∃): represents the statement "there exists." We use to express that there is at least one object in the domain that satisfies a given condition. For example, "∃x P(x)" means "there exists a value x such that P(x) is true."

    +
  2. +
  3. +

    The universal quantifier (∀): represents the statement "for all" or "for every." We use it to express that a given condition is true for every object in the domain. For example, "∀x P(x)" means "for every value x, P(x) is true."

    +
  4. +
+

Rather than exhaustively listing all elements in a domain, quantifiers enable users to make statements about the entire domain at once. This compact representation is crucial when dealing with large or unbounded inputs. Quantifiers also facilitate abstraction and generalization of properties. Instead of specifying properties for specific instances, quantified properties can capture general patterns and behaviors that hold across different objects in a domain. Additionally, by replacing loops in the specification with quantifiers, Kani can encode the properties more efficiently within the specified bounds, making the verification process more manageable and computationally feasible.

+

This new feature doesn't introduce any breaking changes to users. It will only allow them to write properites using the existential (∃) and universal (∀) quantifiers.

+

User Experience

+

We propose a syntax inspired by "Pattern Types". The syntax of existential (i.e., kani::exists) and universal (i.e., kani::forall) quantifiers are:

+
kani::exists(|<var>: <type> [is <range-expr>] | <boolean-expression>)
+kani::forall(|<var>: <type> [is <range-expr>] | <boolean-expression>)
+
+

If <range-expr> is not provided, we assume <var> can range over all possible values of the given <type> (i.e., syntactic sugar for full range |<var>: <type> as .. |). CBMC's SAT backend only supports bounded quantification under constant lower and upper bounds (for more details, see the documentation for quantifiers in CBMC). The SMT backend, on the other hand, supports arbitrary Boolean expressions. In any case, <boolean-expression> should not have side effects, as the purpose of quantifiers is to assert a condition over a domain of objects without altering the state.

+

Consider the following example adapted from the documentation for the from_raw_parts function:

+
use std::ptr;
+use std::mem;
+
+#[kani::proof]
+fn main() {
+    let v = vec![kani::any::<usize>(); 100];
+
+    // Prevent running `v`'s destructor so we are in complete control
+    // of the allocation.
+    let mut v = mem::ManuallyDrop::new(v);
+
+    // Pull out the various important pieces of information about `v`
+    let p = v.as_mut_ptr();
+    let len = v.len();
+    let cap = v.capacity();
+
+    unsafe {
+        // Overwrite memory
+        for i in 0..len {
+            *p.add(i) += 1;
+        }
+
+        // Put everything back together into a Vec
+        let rebuilt = Vec::from_raw_parts(p, len, cap);
+    }
+}
+
+

Given the v vector has non-deterministic values, there are potential arithmetic overflows that might happen in the for loop. So we need to constrain all values of the array. We may also want to check all values of rebuilt after the operation. Without quantifiers, we might be tempted to use loops as follows:

+
use std::ptr;
+use std::mem;
+
+#[kani::proof]
+fn main() {
+    let original_v = vec![kani::any::<usize>(); 100];
+    let v = original_v.clone();
+    for i in 0..v.len() {
+        kani::assume(v[i] < 5);
+    }
+
+    // Prevent running `v`'s destructor so we are in complete control
+    // of the allocation.
+    let mut v = mem::ManuallyDrop::new(v);
+
+    // Pull out the various important pieces of information about `v`
+    let p = v.as_mut_ptr();
+    let len = v.len();
+    let cap = v.capacity();
+
+    unsafe {
+        // Overwrite memory
+        for i in 0..len {
+            *p.add(i) += 1;
+        }
+
+        // Put everything back together into a Vec
+        let rebuilt = Vec::from_raw_parts(p, len, cap);
+        for i in 0..len {
+            assert_eq!(rebuilt[i], original_v[i]+1);
+        }
+    }
+}
+
+

This, however, might unnecessary increase the complexity of the verication process. We can achieve the same effect using quantifiers as shown below.

+
use std::ptr;
+use std::mem;
+
+#[kani::proof]
+fn main() {
+    let original_v = vec![kani::any::<usize>(); 3];
+    let v = original_v.clone();
+    kani::assume(kani::forall(|i: usize is ..v.len() | v[i] < 5));
+
+    // Prevent running `v`'s destructor so we are in complete control
+    // of the allocation.
+    let mut v = mem::ManuallyDrop::new(v);
+
+    // Pull out the various important pieces of information about `v`
+    let p = v.as_mut_ptr();
+    let len = v.len();
+    let cap = v.capacity();
+
+    unsafe {
+        // Overwrite memory
+        for i in 0..len {
+            *p.add(i) += 1;
+        }
+
+        // Put everything back together into a Vec
+        let rebuilt = Vec::from_raw_parts(p, len, cap);
+        assert!(kani::forall(|i: usize is ..len | rebuilt[i] == original_v[i]+1));
+    }
+}
+
+

The same principle applies if we want to use the existential quantifier.

+
use std::ptr;
+use std::mem;
+
+#[kani::proof]
+fn main() {
+    let original_v = vec![kani::any::<usize>(); 3];
+    let v = original_v.clone();
+    kani::assume(kani::forall(|i: usize is ..v.len() | v[i] < 5));
+
+    // Prevent running `v`'s destructor so we are in complete control
+    // of the allocation.
+    let mut v = mem::ManuallyDrop::new(v);
+
+    // Pull out the various important pieces of information about `v`
+    let p = v.as_mut_ptr();
+    let len = v.len();
+    let cap = v.capacity();
+
+    unsafe {
+        // Overwrite memory
+        for i in 0..len {
+            *p.add(i) += 1;
+            if i == 1 {
+              *p.add(i) = 0;
+            }
+        }
+
+        // Put everything back together into a Vec
+        let rebuilt = Vec::from_raw_parts(p, len, cap);
+        assert!(kani::exists(|i: usize is ..len | rebuilt[i] == 0));
+    }
+}
+
+

The usage of quantifiers should be valid in any part of the Rust code analysed by Kani.

+

Detailed Design

+ +

Kani should have the same support that CBMC has for quantifiers. For more details, see Quantifiers.

+

Open questions

+ +
    +
  • Function Contracts RFC - CBMC has support for both exists and forall, but the +code generation is difficult. The most ergonomic and easy way to implement +quantifiers on the Rust side is as higher-order functions taking Fn(T) -> bool, where T is some arbitrary type that can be quantified over. This +interface is familiar to developers, but the code generation is tricky, as +CBMC level quantifiers only allow certain kinds of expressions. This +necessitates a rewrite of the Fn closure to a compliant expression. +
      +
    • Which kind of expressions should be accepted as a "compliant expression"?
    • +
    +
  • +
+

Future possibilities

+ +
    +
  • CBMC has an SMT backend which allows the use of quantifiers with arbitrary Boolean expressions. Kani must include an option for users to experiment with this backend.
  • +
+
+
+
+

Summary

+

A source-based code coverage feature for Kani built on top of Rust's coverage instrumentation.

+

User Impact

+

In our first attempt to add a coverage feature fully managed by Kani, we introduced and made available a line coverage option +(see RFC: Line coverage for more details). +This option has since then allowed us to gather more data around the expectations for a coverage feature in Kani.

+

For example, the line coverage output we produced was not easy to interpret +without knowing some implementation details. +Aside from that, the feature requested in +#2795 alludes to the need +of providing coverage-specific tooling in Kani. +Nevertheless, as captured in +#2640, source-based +coverage results provide the clearest and most precise coverage information.

+

In this RFC, we propose an integration with Rust's source-based code coverage +instrumentation. +This integration would allow us to report source-based code coverage results from Kani. +Also, we propose adding a new user-facing, coverage-focused tool called kani-cov. +The tool would allow users to process coverage results generated by Kani and produce +coverage artifacts such as summaries and reports according to their preferences. +In the next section, we will explain in more detail how we +expect kani-cov to assist with coverage-related tasks.

+

With these changes, we expect our coverage options to become more flexible, +precise and efficient. These options are expected to replace the previous +options available through the line coverage feature. +In the last section of this RFC, we will also discuss +the requirements for a potential integration of this coverage feature with the +LLVM toolchain.

+

User Experience

+

The proposed experience is partially inspired by that of the most popular +coverage frameworks. +First, let us delve into the LLVM coverage workflow, followed by an explanation +of our proposal.

+

The LLVM code coverage workflow

+

The LLVM project is home to one of the most popular code coverage frameworks. +The workflow associated to the LLVM framework is described in the documentation for +source-based code coverage1, +but we briefly describe it here to better relate it with our proposal.

+

In short, the LLVM code coverage workflow follows three steps:

+
    +
  1. Compiling with coverage enabled. This causes the compiler to generate an instrumented program.
  2. +
  3. Running the instrumented program. This generates binary-encoded .profraw files.
  4. +
  5. Using tools to aggregate and export coverage information into other formats.
  6. +
+

When working in a cargo project, step 1 can be done through this command:

+
RUSTFLAGS='-Cinstrument-coverage' cargo build
+
+

The same flag must to be used for step 2:

+
RUSTFLAGS='-Cinstrument-coverage' cargo run
+
+

This should populate the directory with at least one .profraw file. +Each .profraw file corresponds to a specific source code file in your project.

+

At this point, we will have produced the artifacts that we generally require for +the LLVM tools:

+
    +
  1. The instrumented binary which, in addition to the instrumented program, +contains additional information (e.g., the coverage mappings) required to +interpret the profiling results.
  2. +
  3. The .profraw files which essentially includes the profiling results +(e.g., counter values) for each function of the corresponding source code file.
  4. +
+

For step 3, the commands will depend on what kind of results we want. +Most likely we will have to merge the .profraw files and produce a .profdata +file as follows:

+
llvm-profdata merge -sparse *.profraw -o output.profdata
+
+

The resulting .profdata file will contain the aggregated coverage results from +the .profraw files passed to the merge command.

+

Then, we can use a command such as

+
llvm-cov show target/debug/binary —instr-profile=output.profdata -show-line-counts-or-regions
+
+

to visualize the code coverage through the terminal as in the image:

+

Source-based code coverage with llvm-cov

+

or the command

+
llvm-cov report target/debug/binary --instr-profile=output.profdata --show-region-summary
+
+

to produce coverage summaries like this:

+
Filename                                             Regions    Missed Regions     Cover   Functions  Missed Functions  Executed       Lines      Missed Lines     Cover    Branches   Missed Branches     Cover
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+/long/long/path/to/my/project/binary/src/main.rs           9                 3    66.67%           3                 1    66.67%          14                 4    71.43%           0                 0         -
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+TOTAL                                                      9                 3    66.67%           3                 1    66.67%          14                 4    71.43%           0                 0         -
+
+
1 +

The LLVM project refers to their own coverage feature as +source-based code coverage. It is not rare to see the term region +coverage being used instead to refer to the same thing. That is because +LLVM's source-based code coverage feature can report coverage for code +regions, but other coverage frameworks do not support the concept of code +regions.

+
+

The Kani coverage workflow

+

The two main components of the Kani coverage workflow that we propose are the +following:

+
    +
  1. The existing --coverage flag that drives the coverage workflow in Kani, +emits raw coverage data (as in the .profraw files), and produces basic +coverage results by default.
  2. +
  3. A new subcommand cov that allows users to further process raw coverage +information emitted by Kani to produce customized coverage results (i.e., +different to the ones produced by default with the --coverage option). +The cov subcommand is an alias for the kani-cov tool.
  4. +
+

In contrast to the LLVM workflow, where human-readable coverage results can +be produced only after a sequence of LLVM tool commands, we provide some +coverage results by default. +This aligns better with our UX philosophy, and removes the need for a wrapper +around our coverage features like +cargo-llvm-cov. +Alternatively, the cov subcommand offers the ability of producing more +specific coverage results if needed. +We anticipate the cov subcommand being particularly useful in less standard +project setups, giving the users the flexibility required to produce coverage +results tailored to their specific needs.

+

In the following, we describe each one of these components in more detail.

+

The --coverage option

+

The default coverage workflow will be kicked off through the unstable +--coverage option:

+
cargo kani --coverage -Zsource-coverage
+
+

The main difference with respect to the regular verification workflow is that, +at the end of the verification-based coverage run, Kani will generate two coverage +results:

+
    +
  • A coverage summary corresponding to the coverage achieved by the harnesses +included in the verification run. This summary will be printed after the +verification output.
  • +
  • A coverage report corresponding to the coverage achieved by the harnesses +included in the verification run. The report will be placed in the same target +directory where the raw coverage files are put. The path to the report will +also be printed after the verification output.
  • +
+

Therefore, a typical --coverage run could look like this:

+
VERIFICATION:- SUCCESSFUL
+
+Coverage Results:
+
+| Filename | Regions | Missed Regions | Cover | Functions | Missed Functions | Cover |
+| -------- | ------- | -------------- | ----- | --------- | ---------------- | ----- |
+| main.rs  |       9 |              3 | 66.67 |         3 |                1 | 33.33 |
+| file.rs  |      11 |              5 | 45.45 |         2 |                1 | 50.00 |
+
+Coverage report available in target/kani/x86_64-unknown-linux-gnu/cov/kani_2024-04-26_15-30-00/report/index.html
+
+

The cov subcommand

+

The cov subcommand will be used to process raw coverage information generated +by Kani and produce coverage outputs as indicated by the user. +Hence, the cov subcommand corresponds to the set of LLVM tools +(llvm-profdata, llvm-cov, etc.) that are used to produce coverage outputs +through the LLVM coverage workflow.

+

In contrast to LLVM, we will have a single subcommand for all Kani +coverage-related needs. The cov subcommand will just call the kani-cov tool, +which is expected to be shipped along the rest of Kani binaries.

+

We suggest that the subcommand initially offers two options:

+
    +
  1. An option to merge the coverage results from one or more files and coverage +mappings2 into a single file.
  2. +
  3. An option to produce coverage outputs from coverage results, including summaries +or coverage reports in human-readable formats (e.g., HTML).
  4. +
+

Let's assume that we have run cargo kani --coverage -Zsource-coverage and +generated coverage files in the my-coverage folder. Then, we would use cargo kani cov as follows to combine the coverage results3 for all +harnesses:

+
cargo kani cov --merge my-coverage/*.kaniraw -o my-coverage.kanicov
+
+

Let's say the user is first interested in reading a coverage summary through the +terminal. +They can use the --summary option for that:

+
cargo kani cov --summary my-coverage/default.kanimap -instr-profile=my-coverage.kanicov
+
+

The command could print a coverage summary like:

+
| Filename | Regions | Missed Regions | Cover | Functions | ...
+| -------- | ------- | -------------- | ----- | --------- | ...
+| main.rs  |       9 |              3 | 66.67 |         3 | ...
+[...]
+
+

Now, let's say the user wants to produce an HTML report of the coverage results. +They will have to use the --report option for that:

+
cargo kani cov --report my-coverage/default.kanimap -format=html -instr-profile=my-coverage.kanicov -o coverage-report
+
+

This time, the command will generate a coverage-report folder including a +browsable HTML webpage that highlights the regions covered in the source +according to the coverage results in my-coverage.kanicov.

+
4 +

The llvm-cov tool includes the option +gcov to +export into GCC's coverage format Gcov, +and the option +export +to export into the LCOV format. These may be good options to consider for +kani-cov in the future but we should focus on basic formats for now.

+
+
3 +

Options to exclude certain coverage results (e.g, from the +standard library) will likely be part of this option.

+
+
2 +

Coverage mappings essentially provide a snapshot of the source +code reports for items that otherwise are unreachable or have been sliced +away during the compilation process.

+
+

Integration with the Kani VS Code Extension

+

We will update the coverage feature of the +Kani VS Code Extension +to follow this new coverage workflow. +In other words, the extension will first run Kani with the --coverage option and +use kani cov to produce a .kanicov file with the coverage results. +The extension will consume the source-based code coverage results and +highlight region coverage in the source code seen from VS Code.

+

We could also consider other coverage-related features in order to enhance the +experience through the Kani VS Code Extension. For example, we could +automatically show the percentage of covered regions in the status bar by +additionally extracting a summary of the coverage results.

+

Finally, we could also consider an integration with other code coverage tools. +For example, if we wanted to integrate with the VS Code extensions +Code Coverage or +Coverage Gutters, +we would only need to extend kani-cov to export coverage results to the LCOV +format or integrate Kani with LLVM tools as discussed in Integration with LLVM.

+

Detailed Design

+

In this section, we provide more details on:

+
    +
  • The Rust coverage instrumentation and how it can be integrated into +Kani to produce source-based code coverage results.
  • +
  • The proposed coverage workflow to be run by default in Kani when the +--coverage option is used.
  • +
+

This information is mostly intended as a reference for Kani contributors. +Currently, the Rust coverage instrumentation continues to be developed. Because +of that, Rust toolchain upgrades may result in breaking changes to our own +coverage feature. This section should help developers to understand the general +approach and resolve such issues by themselves.

+

The Rust coverage instrumentation

+

The Rust compiler includes two code coverage implementations:

+
    +
  • A source-based coverage implementation which uses LLVM's coverage +instrumentation to generate precise coverage data. This implementation can be +enabled with -C instrument-coverage.
  • +
  • A Gcov-based coverage implementation that derives coverage data based on +DebugInfo. This implementation can be enabled with -Z profile.
  • +
+

The Instrumentation-based Code Coverage +chapter from the rustc book describes in detail how to enable and use the LLVM +instrumentation-based coverage feature. In contrast, the +LLVM Source-Based Code Coverage +chapter from the rustc development guide documents how the LLVM +coverage instrumentation is performed in the Rust compiler.

+

In this section, we will first summarize some information from the +LLVM Source-Based Code Coverage +chapter, limited to details which are relevant to the development of the +source-based coverage feature in Kani. Then, we will explain how Kani taps into +the Rust coverage instrumentation to perform its own coverage instrumentation +and be able to report source-based code coverage results. This will also include +mentions to current issues with this implementation, which we plan to further +discuss in Future possibilities.

+

Understanding the Rust coverage instrumentation

+

The LLVM coverage instrumentation is implemented in the Rust compiler as a +MIR pass called InstrumentCoverage.

+

The MIR pass first builds a coverage-specific version of the MIR Control Flow +Graph (CFG) from the MIR. The initial version of this CFG is based on the MIR's +BasicBlocks, which then gets refined by combining blocks that can be chained +from a coverage-relevant point of view. The final version of the coverage CFG is +then used to determine where to inject the +StatementKind::Coverage +statements in order to measure coverage for a single region coverage span.

+

The injection of StatementKind::Coverage statements is the main result we are +interested in for the integration with Kani. Additionally, the instrumentation +will also attach the +FunctionCoverageInfo +structure to each function's body.5 +This result is also needed at the moment because coverage statements do not +include information on the code region they are supposed to cover. +However, FunctionCoverageInfo contains the +coverage mappings, +which represent the relation between +coverage counters +and code regions.

+

As explained in MIR Pass: +InstrumentCoverage, +many coverage statements will not be converted into a physical +counter6. Instead, they will be converted into a +coverage-counter expression that can be calculated based on other coverage +counters. We highly recommend looking at the example in MIR Pass: +InstrumentCoverage +to better understand how this works. This optimization is mainly done for +performance reasons because incrementing a physical counter causes a +non-negligible overhead, especially within loops.

+

The (StatementKind::)Coverage statements that are injected by the Rust coverage +instrumentation contain a CoverageKind field indicating the type of coverage counter. The variant +CounterIncrement +represents physical counters, while +ExpressionUsed +represents the counter expressions that we just discussed. +Other variants such as SpanMarker or BlockMarker are not relevant to this +work since they should have been erased after the InstrumentCoverage pass.

+
5 +

It is important to note that the StableMIR interface does +not include FunctionCoverageInfo in function bodies. Because of that, we +need to pull it from the internal rustc function bodies.

+
+
6 +

By physical counter, we refer to a global program +variable that is initialized to zero and incremented by one each time that +the execution passes through.

+
+

Integrating the instrumentation into Kani

+

Now that we have explained what the Rust coverage instrumentation does at a high +level, we should be ready to discuss how it can be used from Kani. Here, we will +follow an approach where, during the codegen stage, we generate a Kani +reachability check for each code region and, after the verification stage, we +postprocess the information in those checks to generate the coverage +information. So this section will essentially be a retelling of the +implementation in #3119, and +we will discuss variations/extensions of this approach in the +appropriate sections.

+

Clearly, the first step is adding -C instrument-coverage to the rustc flags +we use when calling the compiler to codegen. This flag enables the Rust coverage +instrumentation that we discussed earlier, resulting in

+
    +
  1. the injection of +Coverage +statements in the MIR code, and
  2. +
  3. the inclusion of FunctionCoverageInfo in function bodies.
  4. +
+

The next step is handling the Coverage statements from codegen_statement.

+

Each Coverage statement contains opaque coverage +information7 of the +CoverageKind +type which can be processed to determine the type of coverage counter +(CounterIncrement for physical counters, ExpressionUsed for counter +expressions) and the ID number of the counter. These two pieces of information allow us to +uniquely identify the counter within a given function. For example, +CounterIncrement(0) would generally refer to the first physical counter in the +function.

+

Unfortunately, the CoverageKind information does not tell us anything about +the code region that the counter covers. However, data about the code region can +be pulled from the coverage mappings included in the FunctionCoverageInfo that +is attached to the (internal) function body. Note that the coverage mappings +includes information about all the coverage counters in a function, even for +counters which have been dropped. Matching the CoverageKind information with +that of the counters in the coverage mappings allows us to retrieve the code +region for any counter.

+

Using all this data, for each coverage statement8 we generate a coverage check +that maintains the essence of the coverage checks described in the RFC for line +coverage:

+
+

Coverage checks are a new class of checks similar to cover checks. +The main difference is that users cannot directly interact with coverage checks (i.e., they cannot add or remove them manually). +Coverage checks are encoded as an assert(false) statement (to test reachability) with a fixed description. +In addition, coverage checks are:

+
    +
  • Hidden from verification results.
  • +
  • Postprocessed to produce coverage results.
  • +
+
+

Therefore, the last step is to postprocess the results from coverage checks to +produce coverage results. This is not too complicated to do since the checks +already include the counter information (type + ID) and the function name in +the check's description. If the span of the code region is also included +(this is what #3119 is +currently doing), we can directly generate a primitive output like this:

+
<file_path> (<function_name>)
+ * <region_start> - <region_end> <status>
+ * ...
+ * <region_start> - <region_end> <status>
+
+

For example, for the test case in +#3119 we report this:

+
src/main.rs (main)
+ * 14:1 - 19:2 COVERED
+
+src/main.rs (test_cov)
+ * 5:1 - 6:15 COVERED
+ * 6:19 - 6:28 UNCOVERED
+ * 7:9 - 7:13 COVERED
+ * 9:9 - 9:14 UNCOVERED
+ * 11:1 - 11:2 COVERED
+
+
+

NOTE: This section has been written according to the implementation in +#3119, which currently +produces a text-based output like the one shown above. There is ongoing work to +store the coverage mappings in a separate file (as described in the next +section), which would save us the need to attach code region data to the +coverage checks.

+
+
7 +

The Rust compiler uses the Opaque type to prevent +others from interfacing with unstable types (e.g., the Coverage type +here). +Nonetheless, this can be worked around by serializing its contents and parsing +it back into an internal data type.

+
+
8 +

We could follow an alternative approach where we +do not instrument each coverage statement, but only those that correspond to +physical counters. Unfortunately, doing so would lead to incorrect coverage +results due to the arithmetic nature of expression-based counters. We elaborate +on this topic in the later parts of this document.

+
+

The default coverage workflow in Kani

+

In this section, we describe the default --coverage workflow from a +developer's point of view. This will hopefully help developers understand how +the different coverage components in Kani are connected. For example, we'll +describe the raw coverage information that gets produced throughout the default +--coverage workflow and define the basic cov commands that it will execute.

+

The main difference with respect to the regular verification workflow is that, +at the end of the verification-based coverage run, Kani will generate two types +of files:

+
    +
  • One single file .kanimap file for the project. This file will contain the +coverage mappings for the project's source code.
  • +
  • One .kaniraw file for each harness. This file will contain the +verification-based results for the coverage-oriented properties corresponding +to a given harness.
  • +
+

Note that .kaniraw files correspond to .profraw files in the LLVM coverage +workflow. Similarly, the .kanimap file corresponds to the coverage-related +information that's embedded into the project's binaries in the LLVM coverage +workflow.9

+

The files will be written into a new timestamped directory associated with the +coverage run. The path to this directory will be printed to standard output in +by default. For example, the draft implementation +writes the coverage files into the target/kani/<target_triple>/cov/ directory.

+

Users aren't expected to read the information in any of these files. +Therefore, there's no need to restrict their format. +The draft implementation +uses the JSON format but we might consider using a binary format if it doesn't +scale.

+

In addition, Kani will produce two types of coverage results:

+
    +
  1. A coverage summary with the default options.
  2. +
  3. A terminal-based coverage report with the default options. However, we will +only do this if the program is composed of a single source +file10.
  4. +
+
9 +

Note that the .kanimap generation isn't implemented in +#3119. The draft +implementation of +kani-cov simply reads +the source files referred to by the code coverage checks, but it doesn't get +information about code trimmed out by the MIR linker.

+
+
10 +

In other words, standalone kani would always emit +these terminal-based reports, but cargo kani would not unless the project +contains a single Rust file (for example, src/main.rs).

+
+

Rationale and alternatives

+

Other coverage implementations

+

In a previous version of this feature, we used an ad-hoc coverage implementation. +In addition to being very inefficient11, the line-based coverage +results were not trivial to interpret by users. +At the moment, there's only another unstable, GCC-compatible code coverage implementation +based on the Gcov format. The Gcov format is line-based so it's not able +to report region coverage results. +In other words, it's not as advanced nor precise as the source-based implementation.

+
11 +

Actual performance benchmarks to follow in +#3119.

+
+

Open questions

+
    +
  • Do we want to instrument dependencies by default? Preliminary benchmarking results show a slowdown of 100% and greater. +More evaluations are required to determine how we handle instrumentation for dependencies, and what options we might want +to provide to users.
  • +
  • How do we handle features/options for kani-cov? In particular, do we need more details in this RFC?
  • +
+

Future possibilities

+

Integration with LLVM

+

As part of this work, we explored a potential integration with the LLVM +framework. The idea behind such an integration would essentially involve +producing coverage results in formats compatible with the LLVM framework (e.g., +the .profraw format). The main advantage of integrating with the LLVM +framework in this way is that we would not need a tool like kani-cov to +aggregate coverage results; we could just use LLVM tools such as llvm-profdata +and llvm-cov to consume them.

+

However, at this time we recommend against integrating with LLVM due to these reasons:

+
    +
  1. Generating the instrumented binary used in the LLVM coverage +workflow requires a standard rustc +compilation with --cfg kani in addition to other flags including -C instrument-coverage. This is likely to result in compilation errors since the +standard rustc backend cannot produce code for Kani APIs, for example.
  2. +
  3. Producing the .profraw files requires executing the instrumented binary at +least once. This would be an issue for Rust projects which assume a particular +environment for their execution.
  4. +
  5. There are no stable interfaces to create or modify files in formats +compatible with the LLVM framework. Even though the documentation for the LLVM +Code Coverage Mapping Format +is excellent, the easiest way to interact with files on these format is through +LLVM tools (e.g., +llvm-cov) +which bring in many other LLVM dependencies. During our exploration, we +attempted to decode and re-encode files in the .profraw to set the counter +data to the values obtained during verification. To this end, we tried tools +like llvm-profparser which +can be used as a replacement for llvm-profdata and llvm-cov but failed to +parse coverage files emitted by the Rust compiler (this is also related to the +next point). Another crate that we used is +coverage-dump, +a recent tool in the Rust compiler used for testing purposes. coverage-dump +extracts coverage mappings from LLVM IR assembly files (i.e., human-readable +*.ll files) but does not work with the binary-encoded formats. Finally, we +also built some ad-hoc tooling to perform these modifications but it soon +became evident that we would need to develop it further in order to handle any +program.
  6. +
  7. LLVM releases a new version approximately every six months. This would +likely result in another "toolchain update" problem for Kani in order to +provide compatibility with newer LLVM versions. Moreover, the Rust compiler +supplies their own version of LLVM tools (rust-profdata, rust-cov, etc.) +which are fully compatible with coverage-related artifacts produced by rustc.
  8. +
+

Optimization with coverage-counter expressions

+

In the subsection related to the +integration, we noted that we could +follow an alternative approach where we only instrument coverage statements that +correspond to physical counters. In fact, this would be the logical choice since +the replacement of physical counters by expression-based counters would also be +a performance optimization for us.

+

However, the expressions used in expression-based counters are built with the +arithmetic operators Add (+) and Sub (-). On the other hand, the +coverage checks performed by Kani have a boolean meaning: you either cover a +region or you do not. Thus, there are many cases where these two notions of +coverage counters are incompatible. For example, let's say we have this +function:

+
fn check_value(val: u32) {
+   if val == VALUE {
+      do_this();
+   } else {
+      do_that();
+   }
+   do_something_else();
+}
+
+

One way to optimize the counters in this function is to have two physical +counters for the branches of the if statement (c1 and c2), and then an +expression-based counter associated to the do_something_else() statement +adding those (i.e., c3 = c1 + c2). If we have, for example, two executions for +this program, with each one taking a different branch, then the results for the +coverage counters will be c1 = 1, c2 = 1 and c3 = c1 + c2 = 2.

+

But what does c3 = 2 mean in the context of a verification-based coverage +result? That is not clear. For instance, in a Kani trace, you could have a +nondeterministic value for val which just happens to be val == VALUE and not +at the same time. This would result in the same counters (c1 = 1, c2 = 1 and +c3 = 2), but the program is being run only once!

+

Note that finding a verification-based replacement for the runtime operators in +counter-based expressions is an interesting research topic. If we could +establish a relation between the runtime and verification expressions, then we +could avoid the instrumentation of coverage checks for expression-based +counters. For example, could we replace the Add operator (+) with an Or +operator (||)? Intuitively, this makes sense since verification-based coverage +counters are binary. It also seems to work for our example since covering any of +the branches should result in the do_something_else() statement being covered +as well, with the counter values now being c1 = 1, c2 = 1 and c3 = 1. +However, it is not clear that this would work for all cases, nor it is clear +that we can replace Sub with another verification-based operator.

+
    +
  • Feature Name: Loop Contracts
  • +
  • Feature Request Issue: #3168
  • +
  • RFC PR: #3167
  • +
  • Status: Under Review
  • +
  • Version: 1
  • +
  • Proof-of-concept:
  • +
+
+

Summary

+

Loop contracts provide way to safely abstract loops of a program, typically +in order to accelerate the verification process, and remove the loop unwinding +bounds. The key idea is to over-approximate the possible set of program states, +while still being precise enough to be able to prove the desired property.

+

User Impact

+

Loop contracts provide an interface for a verified, sound abstraction. +The goal for specifying loop contracts in the source code is two fold:

+
    +
  • Unbounded verification: Currently, proving correctness +(i.e. assertions never fail) on programs with unbounded control flow (e.g. +loops with dynamic bounds) Kani requires unwinding loops for a large number of +times, which is not always feasible. Loop contracts provide a way to abstract +out loops, and hence remove the need for unwinding loops.
  • +
  • Faster CI runs: In most cases, the provided contracts would also significantly +improve Kani's verification time since all loops would be unrolled only to +a single iteration.
  • +
+

Loop contracts are completely optional with no user impact if unused. This +RFC proposes the addition of new attributes, and functions, that shouldn't +interfere with existing functionalities.

+

User Experience

+

A loop contract specifies the behavior of a loop as a boolean predicate +(loop invariants clauses) with certain frames conditions (loop modifies clauses) +that can be checked against the loop implementation, and used to abstract out +the loop in the verification process.

+

We illustrate the usage of loop contracts with an example. +Consider the following program:

+
fn simple_loop() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    while x > 1{
+        x = x - 1;
+    };
+
+    assert!(x == 1);
+}
+
+

The loop in the simple_loop function keep subtracting 1 from x until x is 1. +However, Kani currently needs to unroll the loop for u64::MAX number of times +to verify the assertion at the end of the program.

+

With loop contracts, the user can specify the behavior of the loop as follows:

+
fn simple_loop_with_loop_contracts() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    #[kani::loop_invariant(x >= 1)]
+    while x > 1{
+        x = x - 1;
+    };
+
+    assert!(x == 1);
+}
+
+

The loop invariant clause #[kani::loop_invariant(x >= 1)] specifies the loop +invariants that must hold at the beginning of each iteration of the loop right before +checking the loop guard.

+

In this case, Kani verifies that the loop invariant x >= 1 is inductive, i.e., +x is always greater than or equal to 1 at each iteration before checking x > 1.

+

Also, once Kani proved that the loop invariant is inductive, it can safely use the loop +invariants to abstract the loop out of the verification process. +The idea is, instead of exploring all possible branches of the loop, Kani only needs to +prove those branches reached from an arbitrary program state that satisfies the loop contracts, +after the execution of one iteration of the loop.

+

So, for loops without break statements, we can assume all post-states of the loop satisfying +inv && !loop_guard for proving post-loops properties. +The requirement of satisfying the negation of the loop guard comes from the fact that a path +exits loops without break statements must fail the loop guard.

+

For example, applying loop contracts in simple_loop function is equivalent to the following:

+
fn simple_loop_transformed() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    x = kani::any(); // Arbitrary program state that
+    kani::assume( !(x > 1) && x >= 1); // satisfies !`guard` && `inv` 
+
+    assert!(x == 1);
+}
+
+

The assumption above is actually equivalent to x == 1, hence the assertion at the end +of the program is proved.

+

Write Sets and Havocking

+

For those memory locations that are not modified in the loop, loop invariants state +that they stay unchanged throughout the loop are inductive. In other words, Kani should +only havoc the memory locations that are modified in the loop. This is achieved by +specifying the modifies clause for the loop. For example, the following program:

+
fn simple_loop_two_vars() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+    let mut y: u64 = 1;
+
+    #[kani::loop_invariant(x >= 1)]
+    #[kani::loop_modifies(x)]
+    while x > 1{
+        x = x - 1;
+    };
+
+    assert!(x == 1);
+    assert!(y == 1);
+}
+
+

write to only x in the loop, hence the modifies clause contains only x. +Then when use the loop contracts to abstract the loop, Kani will only havoc the memory +location x and keep y unchanged. Note that if the modifies clause contains also +y, Kani will havoc both x and y, and hence violate the assertion y == 1.

+

Kani can employs CBMC's write set inference to infer the write set of the loop. +So users have to specify the modifies clauses by their self only when the inferred write +sets are not complete---there exists some target that could be written to in the loop but +is not in the inferred write set.

+

Proof of termination

+

Loop contracts also provide a way to prove the termination of the loop. +Without the proof of termination, Kani could report success of some assertions that +are actually unreachable due to non-terminating loops. +For example, consider the following program:

+
fn simple_loop_non_terminating() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    #[kani::loop_invariant(x >= 1)]
+    while true{
+        x = x;
+    };
+
+    assert!(x >= 1);
+}
+
+

After abstracting the loop, the loop will be transformed to no-op, and the assertion +x >= 1 will be proved. However, the loop is actually an infinite loop, and the +assertion will never be reached.

+

For this reason, Kani will also require the user to provide a decreases clause that +specifies a decreasing expression to prove the termination of the loop. For example, in

+
fn simple_loop_terminating() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    #[kani::loop_invariant(x >= 1)]
+    #[kani::loop_decreases(x)]
+    while x > 1{
+        x = x - 1;
+    };
+
+    assert!(x >= 1);
+}
+
+

, the decreases clause #[kani::loop_decreases(x)] specifies that the value of x +decreases at each iteration of the loop, and hence the loop will terminate.

+

Detailed Design

+

Kani implements the functionality of loop contracts in three places.

+
    +
  1. Procedural macros loop_invariant, loop_modifies, and loop_decreases.
  2. +
  3. Code generation for builtin functions expanded from the above macros.
  4. +
  5. GOTO-level loop contracts using CBMC's contract language generated in +kani-compiler.
  6. +
+

Procedural macros loop_invariant, loop_modifies, and loop_decreases.

+

We will implement the three proc-macros loop_invariant, loop_modifies, and loop_decreases to +embed the annotation logic as Rust code. Kani will then compile them into MIR-level code.

+

Code Generation for Builtin Functions

+

Then in the MIR, we codegen the loop contracts as GOTO-level expressions and annotate them +into the corresponding loop latches---the jumps back to the loop head.

+

The artifact goto-instrument in CBMC will extract the loop contracts from the named-subs +of the loop latch, and then apply and prove the extracted loop contracts.

+

GOTO-Level Havocing

+

The ordinary havocing in CBMC is not aware of the type constraints of Rust type. +Hence, we will use customized havocing functions for modifies targets. In detail, +Kani will generate code for the definition of corresponding kani::any() functions +for each modifies target. Then Kani will create a map from the modifies target to the +the name of its kani::any() function, and add the map to the loop latch too.

+

On the CBMC site, goto-instrument will extract the map and instrument the customized +havocing functions for the modifies targets.

+

Rationale and alternatives

+

Rust-Level Transformation vs CBMC

+

Besides transforming the loops in GOTO level using goto-instrument, +we could also do the transformation in Rust level using procedural macros, or +in MIR level.

+

There are two reasons we prefer the GOTO-level transformation. +First, goto-instrument is a mature tool that can correctly instrument the frame +condition checking for the transformed loop, which will save us from reinventing +the error-prone wheel. Second, the loop contracts synthesis tool we developed and +are developing are all based on GOTO level. Hence, doing the transformation in +the GOTO level will make the integration of loop contracts with the synthesis tool +easier.

+

Open questions

+
    +
  • How do we integrate loop contracts with the synthesis tool? When the user-provided +loop contracts are not enough prove the harness, we expect the loop-contract synthesizer +can fix the loop contracts.
  • +
  • How do we translate back modify targets that inferred by CBMC to Rust level?
  • +
  • It is not clear how the CBMC loop modifies inference works for Rust code. We need to +experiment more to decide what would be the best UX of using loop modifies.
  • +
  • How do we handle havocing in unsafe code where it is fine to break the safety invariant +of Rust? In that case, we may need havocing function that preserves validity invariant +but not safety invariant.
  • +
  • What is the proper mechanism for users to specify the loops that they want to opt-out from applying loop contracts, and (optionally) the unwind numbers for them. Such options should be per-harness.
  • +
+

Future possibilities

+
    +
  • We can employ CBMC's decreases inference to infer the decreases clauses to reduce the +user burden of specifying the decreases clauses.
  • +
+ +
+
    +
  • Feature Name: List Subcommand
  • +
  • Feature Request Issue: #2573, #1612
  • +
  • RFC PR: #3463
  • +
  • Status: Unstable
  • +
  • Version: 2
  • +
+
+

Summary

+

Add a subcommand list that, for each crate under verification, lists the information relevant to its verification.

+

User Impact

+

Currently, there is no automated way for a user to gather metadata about Kani's integration with their project. If, for example, a user wants a list of harnesses for their project, they must search for all the relevant contract attributes (currently #[proof] or #[proof_for_contract]) themselves. If done manually, this process is tedious, especially for large projects. Even with a shell script, it is error-prone--if, for example, we introduce a new type of proof harness, users would have to account for it when searching their project.

+

Internally, this feature will be useful for tracking our customers' use of Kani and our progress with standard library verification. Externally, users can leverage this feature to get a high-level view of which areas of their projects have harnesses (and, by extension, which areas are still in need of verification).

+

This feature will not cause any regressions for exisiting users.

+

User Experience

+

Users run a list subcommand, which prints metadata about the harnesses and contracts in each crate under verification. The subcommand takes two options:

+
    +
  • --message-format=[pretty|json]: choose the output format. The default is pretty, which prints to the terminal. The json option creates and writes to a JSON file instead.
  • +
  • --std: this option should be specified when listing the harnesses and contracts in the standard library. This option is only available for kani list (not cargo kani list), which mirrors the verification workflow for the standard library.
  • +
+

This subcommand does not fail. In the case that it does not find any harnesses or contracts, it prints a message informing the user of that fact.

+

Pretty Format

+

The default format, pretty, prints a "Contracts" table and a "Standard Harnesses" list. +Each row of the "Contracts" table consists of a function under contract and its contract harnesses. +The results are printed in lexicographic order.

+

For example:

+
Kani Rust Verifier 0.54.0 (standalone)
+
+Contracts:
+|-------|-------------------------|--------------------------------------------------|
+|       | Function                | Contract Harnesses (#[kani::proof_for_contract]) |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::impl::bar      | example::verify::check_bar                       |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::impl::baz      | example::verify::check_baz                       |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::impl::foo      | example::verify::check_foo_u32                   |
+|       |                         | example::verify::check_foo_u64                   |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::impl::func     | example::verify::check_func                      |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::prep::parse    | NONE                                             |
+|-------|-------------------------|--------------------------------------------------|
+| Total | 5                       | 5                                                |
+|-------|-------------------------|--------------------------------------------------|
+
+Standard Harnesses (#[kani::proof]):
+1. example::verify::check_modify
+2. example::verify::check_new
+
+

All sections will be present in the output, regardless of the result. +If there are no harnesses for a function under contract, Kani inserts NONE in the "Contract Harnesses" column. +If the "Contracts" section is empty, Kani prints a message that "No contracts or contract harnesses were found." +If the "Standard Harnesses" section is empty, Kani prints a message that "No standard harnesses were found."

+

JSON Format

+

If the user wants an output format that's more easily parsed by a script, they can use the json option.

+

The JSON format will contain the same information as the pretty format, with the addition of file paths and file version. +The file version will use semantic versioning. +This way, any users relying on this format for their scripts can detect when we've released a new major version and update their logic accordingly.

+

For example:

+
{
+    kani-version: 0.54,
+    file-version: 0.1,
+    standard-harnesses: [
+        {
+            file: /Users/johnsmith/example/kani_standard_proofs.rs
+            harnesses: [
+                example::verify::check_modify,
+                example::verify::check_new
+            ]
+        },
+    ],
+    contract-harnesses: [
+        {
+            file: /Users/johnsmith/example/kani_contract_proofs.rs
+            harnesses: [
+                example::verify::check_bar,
+                example::verify::check_baz,
+                example::verify::check_foo_u32,
+                example::verify::check_foo_u64, 
+                example::verify::check_func 
+            ]
+        },
+    ],
+    contracts: [
+        {
+            function: example::impl::bar
+            file: /Users/johnsmith/example/impl.rs
+            harnesses: [example::verify::check_bar]
+        },
+        {
+            function: example::impl::baz
+            file: /Users/johnsmith/example/impl.rs
+            harnesses: [example::verify::check_baz]
+        },
+        {
+            function: example::impl::foo
+            file: /Users/johnsmith/example/impl.rs
+            harnesses: [
+                example::verify::check_foo_u32,
+                example::verify::check_foo_u64
+            ]
+        },
+        {
+            function: example::impl::func
+            file: /Users/johnsmith/example/impl.rs
+            harnesses: [example::verify::check_func]
+        },
+        {
+            function: example::prep::parse
+            file: /Users/johnsmith/example/prep.rs
+            harnesses: []
+        }
+    ],
+    totals: {
+        standard-harnesses: 2,
+        contract-harnesses: 5,
+        functions-with-contracts: 5,
+    }
+}
+
+

All sections will be present in the output, regardless of the result. +If there is no result for a given field (e.g., there are no contracts), Kani will output an empty list (or zero for totals).

+

Software Design

+

Driver/Metdata Changes

+

We add a new list subcommand to kani-driver, which invokes the compiler to collect metadata, then post-processes that metadata and outputs the result. +We extend KaniMetadata to include a new field containing each function under contract and its contract harnesses.

+

Compiler Changes

+

In codegen_crate, we update the generation of KaniMetadata to include the new contracts information. +We iterate through each local item in the crate. +Each time we find a function under contract or a contract harness, we include it in the metadata.

+

Rationale and alternatives

+

Users of Kani may have many questions about their project--not only where their contracts and harnesses are, but also where their stubs are, what kinds of contracts they have, etc. Rather than try to answer every question a user might have, which would make the output quite verbose, we focus on these four:

+
    +
  1. Where are the harnesses?
  2. +
  3. Where are the contracts?
  4. +
  5. Which contracts are verified, and by which harnesses?
  6. +
  7. How many harnesses and functions under contract are there?
  8. +
+

We believe these questions are the most important for our use cases of tracking verification progress for customers and the standard library. The UX is designed to answer these questions clearly and concisely.

+

We could have a more verbose or granular output, e.g., printing the metadata on a per-crate or per-module level, or including stubs or other attributes. Such a design would have the benefit of providing more information, with the disadvantage of being more complex to implement and more information for the user to process. +If we do not implement this feature, users will have to obtain this metadata through manual searching, or by writing a script to do it themselves. This feature will improve our internal productivity by automating the process.

+

The Contracts table is close to Markdown, but not quite Markdown--it includes line separators between each row, when Markdown would only have a separator for the header. +We include the separator because without it, it can be difficult to tell from reading the terminal output which entries are in the same row. +The user can transform the table to Markdown by deleting these separators, and we can trivially add a Markdown option in the future if there is demand for it.

+

Open questions

+
    +
  1. Do we want to include more contracts information? We could print more granular information about contracts, e.g., the text of the contracts or the number of contracts.
  2. +
  3. More generally, we could introduce additional options that collect information about other Kani attributes (e.g., stubs). The default would be to leave them out, but this way a user could get more verbose output if they so choose.
  4. +
  5. Do we want to add a filtering option? For instance, --harnesses <pattern> and --contracts <pattern>, where pattern corresponds to a Rust-style path. For example, kani list --harnesses "my_crate::my_module::*" would include all harnesses with that path prefix, while kani list --contracts "my_crate::my_module::*" would include all functions under contract with that path prefix. (If we do this work, we could use it to improve our --harness pattern handling for verification).
  6. +
+

Out of scope / Future Improvements

+

It would be nice to differentiate between regular Kani harnesses and Bolero harnesses. Bolero harnesses invoke Kani using conditional compilation, e.g.:

+
#[cfg_attr(kani, kani::proof)]
+fn check() {
+    bolero::check!()...
+}
+
+

See this blog post for more information.

+

There's no easy way for us to know whether a harness comes from Bolero, since Bolero takes care of rewriting the test to use Kani syntax and invoking the Kani engine. By the time the harness gets to Kani, there's no way for us to tell it apart from a regular harness. Fixing this would require some changes to our Bolero integration.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + diff --git a/rfc/rfcs/0001-mir-linker.html b/rfc/rfcs/0001-mir-linker.html new file mode 100644 index 000000000000..97fda9c67746 --- /dev/null +++ b/rfc/rfcs/0001-mir-linker.html @@ -0,0 +1,405 @@ + + + + + + 0001-mir-linker - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+

Summary

+

Fix linking issues with the rust standard library in a scalable manner by only generating goto-program for +code that is reachable from the user harnesses.

+

User Impact

+

The main goal of this RFC is to enable Kani users to link against all supported constructs from the std library. +Currently, Kani will only link to items that are either generic or have an inline annotation.

+

The approach introduced in this RFC will have the following secondary benefits.

+
    +
  • Reduce spurious warnings about unsupported features for cases where the feature is not reachable from any harness.
  • +
  • In the verification mode, we will likely see a reduction on the compilation time and memory consumption +by pruning the inputs of symtab2gb and goto-instrument. +
      +
    • Compared to linking against the standard library goto-models that take more than 5 GB.
    • +
    +
  • +
  • In a potential assessment mode, only analyze code that is reachable from all public items in the target crate.
  • +
+

One downside is that we will include a pre-compiled version of the std, our release bundle will double in size +(See Rational and Alternatives +for more information on the size overhead). +This will negatively impact the time taken to set up Kani +(triggered by either the first time a user invokes kani | cargo-kani , or explicit invoke the subcommand setup).

+

User Experience

+

Once this RFC has been stabilized users shall use Kani in the same manner as they have been today. +Until then, we wil add an unstable option --mir-linker to enable the cross-crate reachability analysis +and the generation of the goto-program only when compiling the target crate.

+

Kani setup will likely take longer and more disk space as mentioned in the section above. +This change will not be guarded by --mir-linker option above.

+

Detailed Design

+

In a nutshell, we will no longer generate a goto-program for every crate we compile. +Instead, we will generate the MIR for every crate, and we will generate only one goto-program. +This model will only include items reachable from the target crate's harnesses.

+

The current system flow for a crate verification is the following (Kani here represents either kani | cargo-kani +executable):

+
    +
  1. Kani compiles the user crate as well as all its dependencies. +For every crate compiled, kani-compiler will generate a goto-program. +This model includes everything reachable from the crate's public functions.
  2. +
  3. After that, Kani links all models together by invoking goto-cc. +This step will also link against Kani's C library.
  4. +
  5. For every harness, Kani invokes goto-instrument to prune the linked model to only include items reachable from the given harness.
  6. +
  7. Finally, Kani instruments and verify each harness model via goto-instrument and cbmc calls.
  8. +
+

After this RFC, the system flow would be slightly different:

+
    +
  1. Kani compiles the user crate dependencies up to the MIR translation. +I.e., for every crate compiled, kani-compiler will generate an artifact that includes the MIR representation +of all items in the crate.
  2. +
  3. Kani will generate the goto-program only while compiling the target user crate. +It will generate one goto-program that includes all items reachable from any harness in the target crate.
  4. +
  5. goto-cc will still be invoked to link the generated model against Kani's C library.
  6. +
  7. Steps #3 and #4 above will be performed without any change.
  8. +
+

This feature will require three main changes to Kani which are detailed in the sub-sections below.

+

Kani's Sysroot

+

Kani currently uses rustup sysroot to gather information from the standard library constructs. +The artifacts from this sysroot include the MIR for generic items as well as for items that may be included in +a crate compilation (e.g.: functions marked with #[inline] annotation). +The artifacts do not include the MIR for items that have already been compiled to the std shared library. +This leaves a gap that cannot be filled by the kani-compiler; +thus, we are unable to translate these items into goto-program.

+

In order to fulfill this gap, we must compile the standard library from scratch. +This RFC proposes a similar method to what MIRI implements. +We will generate our own sysroot using the -Z always-encode-mir compilation flag. +This sysroot will be pre-compiled and included in our release bundle.

+

We will compile kani's libraries (kani and std) also with -Z always-encode-mir +and with the new sysroot.

+

Cross-Crate Reachability Analysis

+

kani-compiler will include a new reachability module to traverse over the local and external MIR items. +This module will monomorphize all generic code as it's performing the traversal.

+

The traversal logic will be customizable allowing different starting points to be used. +The two options to be included in this RFC is starting from all local harnesses +(tagged with #[kani::proof]) and all public functions in the local crate.

+

The kani-compiler behavior will be customizable via a new flag:

+
--reachability=[ harnesses | pub_fns |  none | legacy | tests ]
+
+

where:

+
    +
  • harnesses: Use the local harnesses as the starting points for the reachability analysis.
  • +
  • pub_fns: Use the public local functions as the starting points for the reachability analysis.
  • +
  • none: This will be the default value if --reachability flag is not provided. It will skip +reachability analysis. No goto-program will be generated. +This will be used to compile dependencies up to the MIR level. +kani-compiler will still generate artifacts with the crate's MIR.
  • +
  • tests: Use the functions marked as tests with #[tests] as the starting points for the analysis.
  • +
  • legacy: Mimics rustc behavior by invoking +rustc_monomorphizer::collect_and_partition_mono_items() to collect the items to be generated. +This will not include many items that go beyond the crate boundary. +This option was only kept for now for internal usage in some of our compiler tests. +It cannot be used as part of the end to end verification flow, and it will be removed in the future.
  • +
+

These flags will not be exposed to the final user. +They will only be used for the communication between kani-driver and kani-compiler.

+

Dependencies vs Target Crate Compilation

+

The flags described in the section above will be used by kani-driver to implement the new system flow. +For that, we propose the following mechanism:

+
    +
  • +

    For standalone kani, we will pass the option --reachability=harnesses to kani-compiler.

    +
  • +
  • +

    For cargo-kani, we will replace

    +
    cargo build <FLAGS>
    +
    +

    with:

    +
    cargo rustc <FLAGS> -- --reachability=harnesses
    +
    +

    to build everything. +This command will compile all dependencies without the --reachability argument, and it will only pass harnesses +value to the compiler when compiling the target crate.

    +
  • +
+

Rational and Alternatives

+

Not doing anything is not an alternative, since this fixes a major gap in Kani's usability.

+

Benefits

+
    +
  • The MIR linker will allow us to fix the linking issues with Rust's standard library.
  • +
  • Once stabilized, the MIR linker will be transparent to the user.
  • +
  • It will enable more powerful and precise static analysis to kani-compiler.
  • +
  • It won't require any changes to our dependencies.
  • +
  • This will fix the harnesses' dependency on the#[no_mangle] annotation +(Issue-661).
  • +
+

Risks

+

Failures in the linking stage would not impact the tool soundness. I anticipate the following failure scenarios:

+
    +
  • ICE (Internal compiler error): Some logic is incorrectly implemented and the linking stage crashes. +Although this is a bad experience for the user, this will not impact the verification result.
  • +
  • Missing items: This would either result in ICE during code generation or a verification failure if the missing +item is reachable.
  • +
  • Extra items: This shouldn't impact the verification results, and they should be pruned by CBMC's reachability +analysis. +This is already the case today. In extreme cases, this could include a symbol that we cannot compile and cause an ICE.
  • +
+

The new reachability code would be highly dependent on the rustc unstable APIs, which could increase +the cost of the upstream synchronization. +That said, the APIs that would be required are already used today.

+

Finally, this implementation relies on a few unstable options from cargo and rustc. +These APIs are used by other tools such as MIRI, so we don't see a high risk that they would be removed.

+

Alternatives

+

The other options explored were:

+
    +
  1. Pre-compile the standard library, and the kani library, and ship the generated *symtab.json files.
  2. +
  3. Pre-compile the standard library, and the kani library, convert the standard library and dependencies to goto-program +(viasymtab2gb) and link them into one single goto-program. +Ship the generated model.
  4. +
+

Both would still require shipping the compiler metadata (via rlib or rmeta) for the kani library, its +dependencies, and kani_macro.so.

+

Both alternatives are very similar. They only differ on the artifact that would be shipped. +They require generating and shipping a custom sysroot; +however, there is no need to implement the reachability algorithm.

+

We implemented a prototype for the MIR linker and one for the alternatives. +Both prototypes generate the sysroot as part of the cargo kani flow.

+

We performed a small experiment (on a c5.4xlarge ec2 instance running Ubuntu 20.04) to assess the options.

+

For this experiment, we used the following harness:

+
#[kani::proof]
+#[kani::unwind(4)]
+pub fn check_format() {
+    assert!("2".parse::<u32>().unwrap() == 2);
+}
+
+

The experiment showed that the MIR linker approach is much more efficient.

+

See the table bellow for the breakdown of time (in seconds) taken for each major step of +the harness verification:

+ + + + + + +
StageMIR LinkerAlternative 1
compilation22.2s64.7s
goto-program generation2.4s90.7s
goto-program linking0.8s33.2s
code instrumentation0.8s33.1
verification0.5s8.5s
+

It is possible that goto-cc time can be improved, but this would also require further experimentation and +expertise that we don't have today.

+

Every option would require a custom sysroot to either be built or shipped with Kani. +The table below shows the size of the sysroot files for the alternative #2 +(goto-program files) vs compiler artifacts (*.rmeta files) +files with -Z always-encode-mir for x86_64-unknown-linux-gnu (on Ubuntu 18.04).

+ + + + +
File TypeRaw sizeCompressed size
symtab.json950M26M
symtab.out84M24M
*.rmeta92M25M
+

These results were obtained by looking at the artifacts generated during the same experiment.

+

Open questions

+
    +
  • Should we build or download the sysroot during kani setup? +We include pre-built MIR artifacts for the std library.
  • +
  • What's the best way to enable support to run Kani in the entire workspace? +We decided to run cargo rustc per package.
  • +
  • Should we codegen all static items no matter what? +We only generate code for static items that are collected by the reachability analysis. +Static objects can only be initialized via constant function. +Thus, it shouldn't have any side effect.
  • +
  • What's the best way to handle cargo kani --tests? +We are going to use the test profile and iterate over all the targets available in the crate: +
      +
    • cargo rustc --profile test -- --reachability=harnesses
    • +
    +
  • +
+

Future possibilities

+
    +
  • Split the goto-program into two or more items to optimize compilation result caching. +
      +
    • Dependencies: One model will include items from all the crate dependencies. +This model will likely be more stable and require fewer updates.
    • +
    • Target crate: The model for all items in the target crate.
    • +
    +
  • +
  • Do the analysis per-harness. This might be adequate once we have a mechanism to cache translations.
  • +
  • Add an option to include external functions to the analysis starting point in order to enable verification when +calls are made from C to rust.
  • +
  • Contribute the reachability analysis code back to upstream.
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0002-function-stubbing.html b/rfc/rfcs/0002-function-stubbing.html new file mode 100644 index 000000000000..02a746999700 --- /dev/null +++ b/rfc/rfcs/0002-function-stubbing.html @@ -0,0 +1,609 @@ + + + + + + 0002-function-stubbing - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+

Summary

+

Allow users to specify that certain functions and methods should be replaced with mock functions (stubs) during verification.

+

Scope

+

In scope:

+
    +
  • Replacing function bodies
  • +
  • Replacing method bodies (which means that the new method body will be executed, whether the method is invoked directly or through a vtable)
  • +
+

Out of scope:

+
    +
  • Replacing type definitions
  • +
  • Replacing macro definitions
  • +
  • Mocking traits
  • +
  • Mocking intrinsics
  • +
+

User impact

+

We anticipate that function/method stubbing will have a substantial positive impact on the usability of Kani:

+
    +
  1. Users might need to stub functions/methods containing features that Kani does not support, such as inline assembly.
  2. +
  3. Users might need to stub functions/methods containing code that Kani supports in principle, but which in practice leads to bad verification performance (for example, if it contains deserialization code).
  4. +
  5. Users could use stubbing to perform compositional reasoning: prove the behavior of a function/method f, and then in other proofs---that call f indirectly---use a stub of f that mocks that behavior but is less complex.
  6. +
+

In all cases, stubbing would enable users to verify code that cannot currently be verified by Kani (or at least not within a reasonable resource bound). +Even without stubbing types, the ability to stub functions/methods can help provide verification-friendly abstractions for standard data structures. +For example, Issue 1673 suggests that some Kani proofs run more quickly if Vec::new is replaced with Vec::with_capacity; function stubbing would allow us to make this substitution everywhere in a codebase (and not just in the proof harness).

+

In what follows, we give an example of stubbing external code, using the annotations we propose in this RFC. +We are able to run this example on a modified version of Kani using a proof-of-concept MIR-to-MIR transformation implementing stubbing (the prototype does not support stub-related annotations; instead, it reads the stub mapping from a file). +This example stubs out a function that returns a random number. +This is the type of function that is commonly stubbed in other verification and program analysis projects, along with system calls, timer functions, logging calls, and deserialization methods---all of which we should be able to handle. +See the appendix at the end of this RFC for an extended example involving stubbing out a deserialization method.

+

Mocking randomization

+

The crate rand is widely used (150M downloads). +However, Kani cannot currently handle code that uses it (Kani users have run into this; see Issue 1727. +Consider this example:

+
#[cfg(kani)]
+#[kani::proof]
+fn random_cannot_be_zero() {
+    assert_ne!(rand::random::<u32>(), 0);
+}
+
+

For unwind values less than 2, Kani encounters an unwinding assertion error (there is a loop used to seed the random number generator); if we set an unwind value of 2, Kani fails to terminate within 5 minutes.

+

Using stubbing, we can specify that the function rand::random should be replaced with a mocked version:

+
#[cfg(kani)]
+fn mock_random<T: kani::Arbitrary>() -> T {
+    kani::any()
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::stub(rand::random, mock_random)]
+fn random_cannot_be_zero() {
+    assert_ne!(rand::random::<u32>(), 0);
+}
+
+

Under this substitution, Kani has a single check, which proves that the assertion can fail. Verification time is 0.02 seconds.

+

User experience

+

This feature is currently limited to stubbing functions and methods. +We anticipate that the annotations we propose here could also be used for stubbing types, although the underlying technical approach might have to change.

+

Stubs will be specified per harness; that is, different harnesses can use different stubs. +This is one of the main design points. +Users might want to mock the behavior of a function within one proof harness, and then mock it a different way for another harness, or even use the original function definition. +It would be overly restrictive to impose the same stub definitions across all proof harnesses. +A good example of this is compositional reasoning: in some harnesses, we want to prove properties of a particular function (and so want to use its actual implementation), and in other harnesses we want to assume that that function has those properties.

+

Users will specify stubs by attaching the #[kani::stub(<original>, <replacement>)] attribute to each harness function. +The arguments original and replacement give the names of functions/methods. +They will be resolved using Rust's standard name resolution rules; this includes supporting imports like use foo::bar as baz, as well as imports of multiple versions of the same crate (in which case a name would resolve to a function/method in a particular version). +The attribute may be specified multiple times per harness, so that multiple (non-conflicting) stub pairings are supported.

+

For example, this code specifies that the function mock_random should be used in place of the function rand::random and the function my_mod::bar should be used in place of the function my_mod::foo for the harness my_mod::my_harness:

+
#[cfg(kani)]
+fn mock_random<T: kani::Arbitrary>() -> T {
+    kani::any()
+}
+
+mod my_mod {
+
+    fn foo(x: u32) -> u32 { ... }
+
+    fn bar(x: u32) -> u32 { ... }
+
+    #[cfg(kani)]
+    #[kani::proof]
+    #[kani::stub(rand::random, super::mock_random)]
+    #[kani::stub(foo, bar)]
+    fn my_harness() { ... }
+
+}
+
+

We will support the stubbing of private functions and methods. +While this provides flexibility that we believe will be necessary in practice, it can also lead to brittle proofs: private functions/methods can change or disappear in even minor version upgrades (thanks to refactoring), and so proofs that depend on them might have a high maintenance burden. +In the documentation, we will discourage stubbing private functions/methods except if absolutely necessary.

+

Stub sets

+

As a convenience, we will provide a macro kani::stub_set that allows users to specify sets of stubs that can be applied to multiple harnesses:

+
kani::stub_set!(my_io_stubs(
+    stub(std::fs::read, my_read),
+    stub(std::fs::write, my_write),
+));
+
+

When declaring a harness, users can use the #[kani::use_stub_set(<stub_set_name>)] attribute to apply the stub set:

+
#[cfg(kani)]
+#[kani::proof]
+#[kani::use_stub_set(my_io_stubs)]
+fn my_harness() { ... }
+
+

The name of the stub set will be resolved through the module path (i.e., they are not global symbols), using Rust's standard name resolution rules.

+

A similar mechanism can be used to aggregate stub sets:

+
kani::stub_set!(all_my_stubs(
+    use_stub_set(my_io_stubs),
+    use_stub_set(my_other_stubs),
+));
+
+

Error conditions

+

Given a set of original-replacement pairs, Kani will exit with an error if

+
    +
  1. a specified original function/method does not exist;
  2. +
  3. a specified replacement stub does not exist;
  4. +
  5. the user specifies conflicting stubs for the same harness (e.g., if the same original function is mapped to multiple replacement functions); or
  6. +
  7. the signature of the replacement stub is not compatible with the signature of the original function/method (see next section).
  8. +
+

Stub compatibility and validation

+

When considering whether a function/method can be replaced with some given stub, we want to allow some measure of flexibility, while also ensuring that we can provide the user with useful feedback if stubbing results in misformed code. +We consider a stub and a function/method to be compatible if all the following conditions are met:

+
    +
  • They have the same number of parameters.
  • +
  • They have the same return type.
  • +
  • Each parameter in the stub has the same type as the corresponding parameter in the original function/method.
  • +
  • The stub must have the same number of generic parameters as the original function/method. +However, a generic parameter in the stub is allowed to have a different name than the corresponding parameter in the original function/method. +For example, the stub bar<A, B>(x: A, y: B) -> B is considered to have a type compatible with the function foo<S, T>(x: S, y: T) -> T.
  • +
  • The bounds for each type parameter don't need to match; however, all calls to the original function must also satisfy the bounds of the stub.
  • +
+

The final point is the most subtle. +We do not require that a type parameter in the signature of the stub implements the same traits as the corresponding type parameter in the signature of the original function/method. +However, Kani will reject a stub if a trait mismatch leads to a situation where a statically dispatched call to a trait method cannot be resolved during monomorphization. +For example, this restriction rules out the following harness:

+
fn foo<T>(_x: T) -> bool {
+    false
+}
+
+trait DoIt {
+    fn do_it(&self) -> bool;
+}
+
+fn bar<T: DoIt>(x: T) -> bool {
+    x.do_it()
+}
+
+#[kani::proof]
+#[kani::stub(foo, bar)]
+fn harness() {
+    assert!(foo("hello"));
+}
+
+

The call to the trait method DoIt::do_it is unresolvable in the stub bar when the type parameter T is instantiated with the type &str. +On the other hand, our approach provides some flexibility, such as allowing our earlier example of mocking randomization: both rand::random and my_random have the type () -> T, but in the first case T is restricted such that the type Standard implements Distribution<T>, whereas in the latter case T has to implement kani::Arbitrary. +This trait mismatch is allowed because at this call site T is instantiated with u32, which implements kani::Arbitrary.

+

Pedagogy

+

To teach this feature, we will update the documentation with a section on function and method stubbing, including simple examples showing how stubbing can help Kani handle code that currently cannot be verified, as well as a guide to best practices for stubbing.

+

Detailed design

+

We expect that this feature will require changes primarily to kani-compiler, with some less invasive changes to kani-driver. +We will modify kani-compiler to collects stub mapping information (from the harness attributes) before code generation. +Since stubs are specified on a per-harness basis, we need to generate multiple versions of code if all harnesses do not agree on their stub mappings; accordingly, we will update kani-compiler to generate multiple versions of code as appropriate. +To do the stubbing, we will plug in a new MIR-to-MIR transformation that replaces the bodies of specified functions with their replacements. +This can be achieved via rustc's query mechanism: if the user wants to replace foo with bar, then when the compiler requests the MIR for foo, we instead return the MIR for bar. +kani-compiler will also be responsible for checking for the error conditions previously enumerated.

+

We will also need to update the metadata that kani-compiler generates, so that it maps each harness to the generated code that has the right stub mapping for that harness (since there will be multiple versions of generated code). +The metadata will also list the stubs applied in each harness. +kani-driver will need to be updated to process this new type of metadata and invoke the correct generated code for each harness. +We can also update the results report to include the stubs that were used.

+

We anticipate that this design will evolve and be iterated upon.

+

Rationale and alternatives: user experience

+

Stubbing is a de facto necessity for verification tools, and the lack of stubbing has a negative impact on the usability of Kani.

+

Benefits

+
    +
  • Because stubs are specified by annotating the harness, the user is able to specify stubs for functions they do not have source access to (like library functions). +This contrasts with annotating the function to be replaced (such as with function contracts).
  • +
  • The current design provides the user with flexibility, as they can specify different sets of stubs to use for different harnesses. +This is important if users are trying to perform compositional reasoning using stubbing, since in some harnesses a function/method should be fully verified, in in other harnesses its behavior should be mocked.
  • +
  • The stub selections are located adjacent to the harness, which makes it easy to understand which replacements are going to happen for each harness.
  • +
+

Risks

+
    +
  • Users can always write stubs that do not correctly correspond to program behavior, and so a successful verification does not actually mean the program is bug-free. +This is similar to other specification bugs. +All the stubbing code will be available, so it is possible to inspect the assumptions it makes.
  • +
+

Comparison to function contracts

+
    +
  • In many cases, stubs are more user-friendly than contracts. +With contracts, it is sometimes necessary to explicitly provide information that is automatically captured in Rust (such as which memory is written). +Furthermore, contract predicates constitute a DSL of their own that needs to be learned; using stubbing, we can stick to using just Rust.
  • +
  • Function contracts sometimes come with a mechanism for verifying that a function satisfies its contract (for example, CBMC provides this). +While we do not plan to provide such a feature, it is possible to emulate this by writing proof harnesses comparing the behavior of the original function and the stub. +Furthermore, our approach provides additional flexibility, as it is not always actually desirable for a stub to be an overapproximation of the function (e.g., we might want the stub to exhibit a certain behavior within a particular harness) or to have a consistent behavior across all harnesses.
  • +
  • The currently proposed function contract mechanism does not provide a way to specify contracts on external functions. +In principle, it would be possible to extend it to do so. +Doing so would require some additional UX design decisions (e.g., "How do users specify this?"); with stubbing there does not need to be a distinction between local and external functions.
  • +
+

Alternative #1: Annotate stubbed functions

+

In this alternative, users add an attribute #[kani::stub_by(<replacement>)] to the function that should be replaced. +This approach is similar to annotating a function with a contract specifying its behavior (the stub acts like a programmatic contract). +The major downside with this approach is that it would not be possible to stub external code. We see this as a likely use case that needs to be supported: users will want to replace std library functions or functions from arbitrary external crates.

+

Alternative #2: Annotate stubs

+

In this alternative, users add an attribute #[kani::stub_of(<original>)] to the stub function itself, saying which function it replaces:

+
#[cfg(kani)]
+#[kani::stub_of(rand::random)]
+fn mock_random<T: kani::Arbitrary>() -> T { ... }
+
+

The downside is that this stub must be uniformly applied across all harnesses and the stub specifications might be spread out across multiple files. +It would also require an extra layer of indirection to use a function as a stub if the user does not have source code access to it.

+

Alternative #3: Annotate harnesses and stubs

+

This alternative combines the proposed solution and Alternative #2. +Users annotate the stub (as in Alternative #2) and specify for each harness which stubs to use using an annotation #[kani::use_stubs(<stub>+)] placed above the harness.

+

This could be combined with modules, so that a module can be used to group stubs together, and then harnesses could pull in all the stubs in the module:

+
#[cfg(kani)]
+mod my_stubs {
+
+  #[kani::stub_of(foo)]
+  fn stub1() { ... }
+
+  #[kani::stub_of(bar)]
+  fn stub2() { ... }
+
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::use_stubs(my_stubs)]
+fn my_harness() { ... }
+
+

The benefit is that stubs are specified per harness, and (using modules) it might be possible to group stubs together. +The downside is that multiple annotations are required and the stub mappings themselves are remote from the harness (at the harness you would know what stub is being used, but not what it is replacing). +There are also several issues that would need to be resolved:

+
    +
  • How do you mock multiple functions with the same stub? +(Say harness A wants to use stub1 to mock foo, and harness B wants to use stub1 to mock bar.)
  • +
  • How do you combine stub sets defined via modules? Would you use the module hierarchy?
  • +
  • If you use modules to define stub sets, are these modules regular modules or not? +In particular, given that modules can contain other constructs than functions, how should we interpret the extra stuff?
  • +
+

Alternative #4: Specify stubs in a file

+

One alternative would be to specify stubs in a file that is passed to kani-driver via a command line option. +Users would specify per-harness stub pairings in the file; JSON would be a possible format. +Using a file would eliminate the need for kani-compiler to do an extra pass to extract harness information from the Rust source code before doing code generation; the rest of the implementation would stay the same. +It would also allow the same harness to be run with different stub selections (by supplying a different file). +The disadvantage is that the stub selection is remote from the harness itself.

+

Rationale and alternatives: stubbing mechanism

+

Our approach is based on a MIR-to-MIR transformation. +Some advantages are that it operates over a relatively simple intermediate representation and rustc has good support for plugging in MIR-to-MIR transformations, so it would not require any changes to rustc itself. +At this stage of the compiler, names have been fully resolved, and there is no problem with swapping in the body of a function defined in one crate for a function defined in another. +Another benefit is that it should be possible to extend the compiler to integrate --concrete-playback with the abstractions (although doing so is out of scope for the current proposal).

+

The major downside with the MIR-to-MIR transformation is that it does not appear to be possible to stub types at that stage (there is no way to change the definition of a type through the MIR). +Thus, our proposed approach will not be a fully general stubbing solution. +However, it is technically feasible and relatively clean, and provides benefits over having no stubbing at all (as can be seen in the examples in the first part of this document).

+

Furthermore, it could be used as part of a portfolio of stubbing approaches, where users stub local types using conditional compilation (see Alternative #1), and Kani provides a modified version of the standard library with verification-friendly versions of types like std::vec::Vec.

+

Alternative #1: Conditional compilation

+

In this baseline alternative, we do not provide any stubbing mechanism at all. +Instead, users can effectively stub local code (functions, methods, and types) using conditional compilation. +For example, they could specify using #[cfg(kani)] to turn off the original definition and turn on the replacement definition when Kani is running, similarly to the ghost state approach taken in the Tokio Bytes proof.

+

The disadvantage with this approach is that it does not provide any way to stub external code, which is one of the main motivations of our proposed approach.

+

Alternative #2: Source-to-source transformation

+

In this alternative, we rewrite the source code before it even gets to the compiler. +The advantage with this approach is that it is very flexible, allowing us to stub functions, methods, and types, either by directly replacing them, or appending their replacements and injecting appropriate conditional compilation guards.

+

This approach entails less user effort than Alternative #1, but it has the same downside that it requires all source code to be available. +It also might be difficult to inject code in a way that names are correctly resolved (e.g., if the replacement code comes from a different crate). +Also, source code is difficult to work with (e.g., unexpanded macros).

+

On the last two points, we might be able to take advantage of an existing source analysis platform like rust-analyzer (which has facilities like structural search replace), but this would add more (potentially fragile) dependencies to Kani.

+

Alternative #3: AST-to-AST or HIR-to-HIR transformation

+

In this alternative, we implement stubbing by rewriting the AST or High-Level IR (HIR) of the program. +The HIR is a more compiler-friendly version of the AST; it is what is used for type checking. +To swap out a function, method, or type at this level, it looks like it would be necessary to add another pass to rustc that takes the initial AST/HIR and produces a new AST/HIR with the appropriate replacements.

+

The advantage with this approach is, like source transformations, it would be very flexible. +The downside is that it would require modifying rustc (as far as we know, there is not an API for plugging in a new AST/HIR pass), and would also require performing the transformations at a very syntactic level: although the AST/HIR would likely be easier to work with than source code directly, it is still very close to the source code and not very abstract. +Furthermore, provided we supported stubbing across crate boundaries, it seems like we would run into a sequencing issue: if we were trying to stub a function in a dependency, we might not know until after we have compiled that dependency that we need to modify its AST/HIR; furthermore, even if we were aware of this, the replacement AST/HIR code would not be available at that time (the AST/HIR is usually just constructed for the crate currently being compiled).

+

Open questions

+
    +
  • Would there ever be the need to stub a particular monomorphization of a function, as opposed to the polymorphic function?
  • +
  • How can the user verify that the stub is an abstraction of the original function/method? +Sometimes it might be important that a stub is an overapproximation or underapproximation of the replaced code. +One possibility would be writing proofs about stubs (possibly relating their behavior to that of the code they are replacing).
  • +
+

Limitations

+
    +
  • Our proposed approach will not work with --concrete-playback (for now).
  • +
  • We are only able to apply abstractions to some dependencies if the user enables the MIR linker.
  • +
+

Future possibilities

+
    +
  • +

    It would increase the utility of stubbing if we supported stubs for types. +The source code annotations could likely stay the same, although the underlying technical approach performing these substitutions might be significantly more complex.

    +
  • +
  • +

    It would probably make sense to provide a library of common stubs for users, since many applications might want to stub the same functions and mock the same behaviors (e.g., rand::random can be replaced with a function returning kani::any).

    +
  • +
  • +

    We could provide special classes of stubs that are likely to come up in practice:

    +
      +
    • unreachable: assert the function is unreachable.
    • +
    • havoc_locals: return nondeterministic values and assign nondeterministic values to all mutable arguments.
    • +
    • havoc: similar to havoc_locals but also assign nondeterministic values to all mutable global variables.
    • +
    • uninterpret: treat function as an uninterpreted function.
    • +
    +
  • +
  • +

    How can we provide a good user experience for accessing private fields of self in methods? +It is possible to do so using std::mem::transmute (see below); this is clunky and error-prone, and it would be good to provide better support for users.

    +
    struct Foo {
    +    x: u32,
    +}
    +
    +impl Foo {
    +    pub fn m(&self) -> u32 {
    +        0
    +    }
    +}
    +
    +struct MockFoo {
    +    pub x: u32,
    +}
    +
    +fn mock_m(foo: &Foo) {
    +    let mock: &MockFoo = unsafe { std::mem::transmute(foo) };
    +    return mock.x;
    +}
    +
    +#[cfg(kani)]
    +#[kani::proof]
    +#[kani::stub(Foo::m, mock_m)]
    +fn my_harness() { ... }
    +
    +
  • +
+

Appendix: an extended example

+

In this example, we mock a serde_json (96M downloads) deserialization method so that we can prove a property about the following Firecracker function that parses a configuration from some raw data:

+
fn parse_put_vsock(body: &Body) -> Result<ParsedRequest, Error> {
+    METRICS.put_api_requests.vsock_count.inc();
+    let vsock_cfg = serde_json::from_slice::<VsockDeviceConfig>(body.raw()).map_err(|err| {
+        METRICS.put_api_requests.vsock_fails.inc();
+        err
+    })?;
+
+    // Check for the presence of deprecated `vsock_id` field.
+    let mut deprecation_message = None;
+    if vsock_cfg.vsock_id.is_some() {
+        // vsock_id field in request is deprecated.
+        METRICS.deprecated_api.deprecated_http_api_calls.inc();
+        deprecation_message = Some("PUT /vsock: vsock_id field is deprecated.");
+    }
+
+    // Construct the `ParsedRequest` object.
+    let mut parsed_req = ParsedRequest::new_sync(VmmAction::SetVsockDevice(vsock_cfg));
+    // If `vsock_id` was present, set the deprecation message in `parsing_info`.
+    if let Some(msg) = deprecation_message {
+        parsed_req.parsing_info().append_deprecation_message(msg);
+    }
+
+    Ok(parsed_req)
+}
+
+

We manually mocked some of the Firecracker types with simpler versions to reduce the number of dependencies we had to pull in (e.g., we removed some enum variants, unused struct fields). +With these changes, we were able to prove that the configuration data has a vsock ID if and only if the parsing metadata includes a deprecation message:

+
#[cfg(kani)]
+fn get_vsock_device_config(action: RequestAction) -> Option<VsockDeviceConfig> {
+    match action {
+        RequestAction::Sync(vmm_action) => match *vmm_action {
+            VmmAction::SetVsockDevice(dev) => Some(dev),
+            _ => None,
+        },
+        _ => None,
+    }
+}
+
+#[cfg(kani)]
+#[kani::proof]
+#[kani::unwind(2)]
+#[kani::stub(serde_json::deserialize_slice, mock_deserialize)]
+fn test_deprecation_vsock_id_consistent() {
+    // We are going to mock the parsing of this body, so might as well use an empty one.
+    let body: Vec<u8> = Vec::new();
+    if let Ok(res) = parse_put_vsock(&Body::new(body)) {
+        let (action, mut parsing_info) = res.into_parts();
+        let config = get_vsock_device_config(action).unwrap();
+        assert_eq!(
+            config.vsock_id.is_some(),
+            parsing_info.take_deprecation_message().is_some()
+        );
+    }
+}
+
+

Crucially, we did this by stubbing out serde_json::from_slice and replacing it with our mock version below, which ignores its input and creates a "symbolic" configuration struct:

+
#[cfg(kani)]
+fn symbolic_string(len: usize) -> String {
+    let mut v: Vec<u8> = Vec::with_capacity(len);
+    for _ in 0..len {
+        v.push(kani::any());
+    }
+    unsafe { String::from_utf8_unchecked(v) }
+}
+
+#[cfg(kani)]
+fn mock_deserialize(_data: &[u8]) -> serde_json::Result<VsockDeviceConfig> {
+    const STR_LEN: usize = 1;
+    let vsock_id = if kani::any() {
+        None
+    } else {
+        Some(symbolic_string(STR_LEN))
+    };
+    let guest_cid = kani::any();
+    let uds_path = symbolic_string(STR_LEN);
+    let config = VsockDeviceConfig {
+        vsock_id,
+        guest_cid,
+        uds_path,
+    };
+    Ok(config)
+}
+
+

The proof takes 170 seconds to complete (using Kissat as the backend SAT solver for CBMC).

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0003-cover-statement.html b/rfc/rfcs/0003-cover-statement.html new file mode 100644 index 000000000000..2c3596e80cbe --- /dev/null +++ b/rfc/rfcs/0003-cover-statement.html @@ -0,0 +1,321 @@ + + + + + + 0003-cover-statement - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+

Summary

+

A new Kani API that allows users to check that a certain condition can occur at a specific location in the code.

+

User Impact

+

Users typically want to gain confidence that a proof checks what it is supposed to check, i.e. that properties are not passing vacuously due to an over-constrained environment.

+

A new Kani macro, cover will be created that can be used for checking that a certain condition can occur at a specific location in the code. +The purpose of the macro is to verify, for example, that assumptions are not ruling out those conditions, e.g.:

+
let mut v: Vec<i32> = Vec::new();
+let len: usize = kani::any();
+kani::assume(len < 5);
+for _i in 0..len {
+    v.push(kani::any());
+}
+// make sure we can get a vector of length 5
+kani::cover!(v.len() == 5);
+
+

This is typically used to ensure that verified checks are not passing vacuously, e.g. due to overconstrained pre-conditions.

+

The special case of verifying that a certain line of code is reachable can be achieved using kani::cover!() (which is equivalent to cover!(true)), e.g.

+
match x {
+    val_1 => ...,
+    val_2 => ...,
+    ...
+    val_i => kani::cover!(), // verify that `x` can take the value `val_i`
+}
+
+

Similar to Rust's assert macro, a custom message can be specified, e.g.

+
kani::cover!(x > y, "x can be greater than y");
+
+

User Experience

+

The cover macro instructs Kani to find at least one possible execution that satisfies the specified condition at that line of code. If no such execution is possible, the check is reported as unsatisfiable.

+

Each cover statement will be reported as a check whose description is cover condition: cond and whose status is:

+
    +
  • SATISFIED (green): if Kani found an execution that satisfies the condition.
  • +
  • UNSATISFIABLE (yellow): if Kani proved that the condition cannot be satisfied.
  • +
  • UNREACHABLE (yellow): if Kani proved that the cover statement itself cannot be reached.
  • +
+

For example, for the following cover statement:

+
kani::cover!(a == 0);
+
+

An example result is:

+
Check 2: main.cover.2
+         - Status: SATISFIED
+         - Description: "cover condition: a == 0"
+         - Location: foo.rs:9:5 in function main
+
+

Impact on Overall Verification Status

+

By default, unsatisfiable and unreachable cover checks will not impact verification success or failure. +This is to avoid getting verification failure for harnesses for which a cover check is not relevant. +For example, on the following program, verification should not fail for another_harness_that_doesnt_call_foo because the cover statement in foo is unreachable from it.

+
[kani::proof]
+fn a_harness_that_calls_foo() {
+    foo();
+}
+
+#[kani::proof]
+fn another_harness_that_doesnt_call_foo() {
+    // ...
+}
+
+fn foo() {
+    kani::cover!( /* some condition */);
+}
+
+

The --fail-uncoverable option will allow users to fail the verification if a cover property is unsatisfiable or unreachable. +This option will be integrated within the framework of Global Conditions, which is used to define properties that depend on other properties.

+

Using the --fail-uncoverable option will enable the global condition with name fail_uncoverable. +Following the format for global conditions, the outcome will be one of the following:

+
    +
  1. - fail_uncoverable: FAILURE (expected all cover statements to be satisfied, but at least one was not)
  2. +
  3. - fail_uncoverable: SUCCESS (all cover statements were satisfied as expected)
  4. +
+

Note that the criteria to achieve a SUCCESS status depends on all properties of the "cover" class having a SATISFIED status. +Otherwise, we return a FAILURE status.

+

Inclusion in the Verification Summary

+

Cover checks will be reported separately in the verification summary, e.g.

+
SUMMARY:
+ ** 1 of 206 failed (2 unreachable)
+ Failed Checks: assertion failed: x[0] == x[1]
+
+ ** 30 of 35 cover statements satisfied (1 unreachable) <--- NEW
+
+

In this example, 5 of the 35 cover statements were found to be unsatisfiable, and one of those 5 is additionally unreachable.

+

Interaction with Other Checks

+

If one or more unwinding assertions fail or an unsupported construct is found to be reachable (which indicate an incomplete path exploration), and Kani found the condition to be unsatisfiable or unreachable, the result will be reported as UNDETERMINED.

+

Detailed Design

+

The implementation will touch the following components:

+
    +
  • Kani library: The cover macro will be added there along with a cover function with a rustc_diagnostic_item
  • +
  • kani-compiler: The cover function will be handled via a hook and codegen as two assertions (cover(cond) will be codegen as __CPROVER_assert(false); __CPROVER_assert(!cond)). +The purpose of the __CPROVER_assert(false) is to determine whether the cover statement is reachable. +If it is, the second __CPROVER_assert(!cond) indicates whether the condition is satisfiable or not.
  • +
  • kani-driver: The CBMC output parser will extract cover properties through their property class, and their result will be set based on the result of the two assertions: +
      +
    • The first (reachability) assertion is proven: report as FAILURE (UNREACHABLE)
    • +
    • The first assertion fails, and the second one is proven: report as FAILURE to indicate that the condition is unsatisfiable
    • +
    • The first assertion fails, and the second one fails: report as SUCCESS to indicate that the condition is satisfiable
    • +
    +
  • +
+

Rationale and alternatives

+
    +
  • +

    What are the pros and cons of this design? +CBMC has its own cover API (__CPROVER_cover), for which SUCCESS is reported if an execution is found, and FAILURE is reported otherwise. +However, using this API currently requires running CBMC in a separate "cover" mode. +Having to run CBMC in that mode would complicate the Kani driver as it will have to perform two CBMC runs, and then combine their results into a single report. +Thus, the advantage of the proposed design is that it keeps the Kani driver simple. +In addition, the proposed solution does not depend on a feature in the backend, and thus will continue to work if we were to integrate a different backend.

    +
  • +
  • +

    What is the impact of not doing this? +The current workaround to accomplish the same effect of verifying that a condition can be covered is to use assert!(!cond). +However, if the condition can indeed be covered, verification would fail due to the failure of the assertion.

    +
  • +
+

Open questions

+
    +
  • ~Should we allow format arguments in the macro, e.g. kani::cover!(x > y, "{} can be greater than {}", x, y)? +Users may expect this to be supported since the macro looks similar to the assert macro, but Kani doesn't include the formatted string in the message description, since it's not available at compile time.~ +
      +
    • For now, this macro will not accept format arguments, since this +is not well handled by Kani. +This is an extesion to this API that can be easily added later on if Kani +ever supports runtime formatting.
    • +
    +
  • +
+

Other Considerations

+

We need to make sure the concrete playback feature can be used with cover statements that were found to be coverable.

+

Future possibilities

+

The new cover API subsumes the current kani::expect_fail function. +Once it's implemented, we should be able to get rid of expect_fail, and all the related code in compiletest that handles the EXPECTED FAILURE message in a special manner.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0004-loop-contract-synthesis.html b/rfc/rfcs/0004-loop-contract-synthesis.html new file mode 100644 index 000000000000..a22cd74a2c8e --- /dev/null +++ b/rfc/rfcs/0004-loop-contract-synthesis.html @@ -0,0 +1,377 @@ + + + + + + 0004-loop-contract-synthesis - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+

Summary

+

A new option that allows users to verify programs without unwinding loops by synthesizing loop contracts for those loops.

+

User Impact

+

Currently Kani does not support verification on programs with unbounded control flow (e.g. loops with dynamic bounds). +Kani unrolls all unbounded loops until a global threshold (unwinding number) specified by the user and then verifies this unrolled program, which limits the set of programs it can verify.

+

A new Kani flag --synthesize-loop-contracts will be created that can be used to enable the goto-level loop-contract synthesizer goto-synthesizer. +The idea of loop contracts is, instead of unwinding loops, we abstract those loops as non-loop structures that can cover arbitrary iterations of the loops. +The loop contract synthesizer, when enabled, will attempt to synthesize loop contracts for all loops. +CBMC can then apply the synthesized loop contracts and verify the program without unwinding any loop. +So, the synthesizer will help to verify the programs that require Kani to unwind loops for a very large number of times to cover all iterations.

+

For example, the number of executed iterations of the loop in the following harness is dynamically bounded by an unbounded variable y 1. +Only an unwinding value of i32::MAX can guarantee to cover all iterations of the loop, and hence satisfy the unwinding assertions. +Unwinding the loop an i32::MAX number of times will result in a too large goto program to be verified by CBMC.

+
#[kani::proof]
+fn main() {
+    let mut y: i32 = kani::any_where(|i| *i > 0);
+
+    while y > 0 {
+        y = y - 1;
+    }
+    assert!(y == 0);
+}
+
+

With the loop-contract synthesizer, Kani can synthesize the loop invariant y >= 0, with which it can prove the post-condition y == 0 without unwinding the loop.

+

Also, loop contracts could improve Kani’s verification time since all loops will be abstracted to a single iteration, as opposed to being unwound a large number of iterations. +For example, we can easily find out that the following loop is bounded by an unwinding value of 5000. +Kani can verify the program in a few minutes by unwinding the loop 5000 times. +With loop contracts, we only need to verify the single abstract iteration of the loop, which leads to a smaller query. +As a result, Kani with the synthesizer can verify the program in a few seconds.

+
#[kani::proof]
+#[kani::unwind(5000)]
+fn main() {
+    let mut y: i32 = 5000;
+
+    while y > 0 {
+        y = y - 1;
+    }
+    assert!(y == 0);
+}
+
+

The goto-synthesizer is an enumeration-based synthesizer. +It enumerates candidate invariants from a pre-designed search space described by a given regular tree grammar and verifies if the candidate is an inductive invariant. +Therefore it has the following limitations:

+
    +
  1. the search space is not complete, so it may fail to find a working candidate. The current search space consists of only conjunctions of linear inequalities built from the variables in the loop, which is not expressive enough to capture all loop invariants. +For example, the loop invariant a[i] == 0 contains an array access and cannot be captured by the current search space. +However, we can easily extend the search space to include more complex expressions with the cost of an exponential increase of the running time of the synthesizer.
  2. +
  3. the synthesizer suffers from the same limitation as the loop contract verification in CBMC. For example, it does not support unbounded quantifiers, or dynamic allocations in the loop body.
  4. +
+

User Experience

+

Users will be able to use the new command-line flag --synthesize-loop-contracts to run the synthesizer, which will attempt to synthesize loop contracts, and verify programs with the synthesized loop contracts.

+

Limit Resource Used by Synthesizer for Termination

+

Without a resource limit, an enumerative synthesizer may run forever to exhaust a search space consisting of an infinite number of candidates, especially when there is no solution in the search space. +So, for the guarantee of termination, we provide users options: --limit-synthesis-time T to limit the running time of the synthesizer to be less than T seconds.

+

Output of Kani when the Synthesizer is Enabled

+

When the flag --synthesize-loop-contracts is provided, Kani will report different result for different cases

+
    +
  1. When there exists some loop invariant in the candidate space with which all assertions can be proved, Kani will synthesize the loop contracts, verify the program with the synthesized loop contracts, and report verification SUCCESS;
  2. +
  3. When no working candidate has been found in the search space within the specified limits, Kani will report the verification result with the best-effort-synthesized loop contracts. +Note that as loop contracts are over-approximations of the loop, the violated assertions in this case may be spurious. +So we will report the violated assertions as UNDETERMINED instead of FAILED.
  4. +
+

A question about how do we print the synthesized loop contracts when users request is discussed in Open question.

+

Detailed Design

+

The synthesizer goto-synthesizer is implemented in the repository of CBMC, takes as input a goto binary, and outputs a new goto binary with the synthesized loop contracts applied. +Currently, Kani invokes goto-instrument to instrument the goto binary main.goto into a new goto binary main_instrumented.goto, and then invokes cbmc on main_instrumented.goto to get the verification result. +The synthesis will happen between calling goto-instrument and calling cbmc. +That is, we invoke goto-synthesizer on main_instrumented.goto to produce a new goto binary main_synthesized.goto, and then call cbmc on main_synthesized.goto instead.

+

When invoking goto-synthesizer, we pass the following parameters to it with the flags built in goto-synthesizer:

+
    +
  • the resource limit of the synthesis;
  • +
  • the solver options to specify what SAT solver we use to verify invariant candidates.
  • +
+

The enumerator used in the synthesizer enumerates candidates from the language of the following grammar template.

+
NT_Bool -> NT_Bool && NT_Bool | NT_int == NT_int 
+            | NT_int <= NT_int | NT_int < NT_int 
+            | SAME_OBJECT(terminals_ptr, terminals_ptr)
+            
+NT_int  -> NT_int + NT_int | terminals_int | LOOP_ENTRY(terminals_int)
+            | POINTER_OFFSET(terminals_ptr) | OBJECT_SIZE(terminals_ptr)
+            | POINTER_OFFSET(LOOP_ENTRY(terminals_ptr)) | 1
+
+

where terminals_ptr are all pointer variables in the scope, and terminal_int are all integer variables in the scope. +For every candidate invariant, goto-synthesizer applies it to the GOTO program and runs CBMC to verify the program.

+
    +
  • If all checks in the program pass, goto-synthesizer returns it as a solution.
  • +
  • If the inductive checks pass but some of the other checks fail, the candidate invariant is inductive. +We keep it as an inductive invariant clause.
  • +
  • If the inductive checks fail, we discard the candidate. +When the resource limit is reached, goto-synthesizer returns the conjunction of all inductive clauses as the best-effort-synthesized loop contracts.
  • +
+

We use the following example to illustrate how the synthesizer works.

+
#[kani::proof]
+fn main() {
+    let mut y: i32 = kani::any_where(|i| *i > 0);
+
+    while y > 0 {
+        y = y - 1;
+    }
+    assert!(y == 0);
+}
+
+

As there is only one variable y in the scope, the grammar template above will be instantiated to the following grammar

+
NT_Bool -> NT_Bool && NT_Bool | NT_int == NT_int 
+            | NT_int <= NT_int | NT_int < NT_int 
+NT_int  -> NT_int + NT_int | y | LOOP_ENTRY(y) | 1
+
+

The synthesizer will enumerate candidates derived from NT_Bool in the following order.

+
y == y
+y == LOOP_ENTRY(y)
+y == 1
+...
+1 <= y + 1
+...
+
+

The synthesizer then verifies with CBMC if the candidate is an inductive invariant that can be used to prove the post-condition y == 0. +For example, the candidate y == y is verified to be an inductive invariant, but cannot be used to prove the post-condition y == 0. +The candidate y == 1 is not inductive. +The synthesizer rejects all candidates until it finds the candidate 1 <= y + 1, which can be simplified to y >= 0. +y >= 0 is an inductive invariant that can be used to prove the post-condition. +So the synthesizer will return y >= 0 and apply it to the goto model to get main_synthesized.goto.

+

For assign clauses, the synthesizer will first use alias analysis to determine an initial set of assign targets. +During the following iteration, if any assignable-check is violated, the synthesizer will extract the assign target from the violated check.

+

Then Kani will call cbmc on main_synthesized.goto to verify the program with the synthesized loop contracts.

+

Rationale and alternatives

+
    +
  • Different candidate space. +The candidate grammar introduced above now only contains a restricted set of operators, which works well for array-manipulating programs with only pointer-checks instrumented by goto-instrument, but probably not enough for other user-written checks. +We may want to include array-indexing, pointer-dereference, or other arithmetic operators in the candidate grammar for synthesizing a larger set of loop invariants. +However, there is a trade-off between the size of candidate we enumerate and the running time of the enumeration. +We will collect more data to decide what operators we should include in the candidate grammar. +Once we decide more kinds of candidate grammars, we will provide users options to choose which candidate grammar they want to use.
  • +
+

Open questions

+

How does the synthesizer work with unwinding numbers? +There may exist some loops for which the synthesizer cannot find loop contracts, but some small unwinding numbers are enough to cover all executions of the loops. +In this case, we may want to unwind some loops in the program while synthesizing loop contracts for other loops. +It requires us to have a way to identify and specify which loops we want to unwind.

+

In C programs, we identify loops by the loop ID, which is a pair (function name, loop number). +However, in Rust programs, loops are usually in library functions such as Iterator::for_each. +And a library function may be called from different places in the program. +We may want to unwind the loop in some calls but not in other calls.

+

How do we output the synthesized loop contracts? +To better earn users' trust, we want to be able to report what loop contracts we synthesized and used to verify the given programs. +Now goto-synthesizer can dump the synthesized loop contracts into a JSON file. +Here is an example of the dumped loop contracts. +It contains the location of source files of the loops, the synthesized invariant clauses and assign clauses for loops identified by loop numbers.

+
{
+    "sources": [ "/Users/qinhh/Repos/playground/kani/synthesis/base_2/test.rs" ],
+    "functions": [
+      {
+        "main": [ "loop 1 invariant y >= 0", 
+                  "loop 1 assigns var_9,var_10,var_11,x,y,var_12" ]
+      }
+    ],
+    "output": "stdout"
+}
+
+

There are two challenges here if we want to also dump synthesized loop contracts in Kani.

+
    +
  1. We need to have a consistent way to identify loops.
  2. +
  3. We need to dump loop invariants in rust instead of c.
  4. +
  5. There are many auxiliary variables we added in Kani-compiled GOTO, such as var_9, var_10, var_11, and var_12 in the above JSON file. +We need to translate them back to the original variables they represent.
  6. +
+

Future possibilities

+

User-provided loop contracts. +If we have a good answer for how to identify loops and dump synthesized loop contracts, we could probably also allow users to provide the loop contracts they wrote to Kani, and verify programs with user-provided loop contracts.

+

When users want to unwind some loops, we can also introduce macros to enable/disable unwinding for certain block of code.

+
#[kani::proof]
+#[kani::unwind(10)]
+fn check() {
+    // unwinding starts as enabled, so all loops in this code block will be unwound to 10
+    #[kani::disable_unwinding]
+    // unwinding is disabled for all loops in this block of code
+    #[kani::enable_unwinding]
+    // it is enabled in this block of code until the end of the program
+}
+
+

Invariant caching. +The loop invariant could be broken when users modify their code. +However, we could probably cache previously working loop invariants and attempt to reuse them when users modify their code. +Even if the cached loop invariants are not enough to prove the post-condition, they could still be used as a starting point for the synthesizer to find new loop invariants.

+
1 +

We say an integer variable is unbounded if there is no other bound on its value besides the width of its bit-vector representation.

+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0005-should-panic-attr.html b/rfc/rfcs/0005-should-panic-attr.html new file mode 100644 index 000000000000..81a4bebd5c80 --- /dev/null +++ b/rfc/rfcs/0005-should-panic-attr.html @@ -0,0 +1,408 @@ + + + + + + 0005-should-panic-attr - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ + +
+

Summary

+

Users may want to express that a verification harness should panic. +This RFC proposes a new harness attribute #[kani::should_panic] that informs Kani about this expectation.

+

User Impact

+

Users may want to express that a verification harness should panic. +In general, a user adding such a harness wants to demonstrate that the verification fails because a panic is reachable from the harness.

+

Let's refer to this concept as negative verification, +so the relation with negative testing becomes clearer. +Negative testing can be exercised in Rust unit tests using the #[should_panic] attribute. +If the #[should_panic] attribute is added to a test, cargo test will check that the execution of the test results in a panic. +This capability doesn't exist in Kani at the moment, but it would be useful for the same reasons +(e.g., to show that invalid inputs result in verification failures, or increase the overall verification coverage).

+

We propose an attribute that allows users to exercise negative verification in Kani.

+

We also acknowledge that, in other cases, users may want to express more granular expectations for their harnesses. +For example, a user may want to specify that a given check is unreachable from the harness. +An ergonomic mechanism for informing Kani about such expectations is likely to require other improvements in Kani (a comprehensive classification for checks reported by Kani, a language to describe expectations for checks and cover statements, and general output improvements). +Moving forward, we consider that such a mechanism and this proposal solve different problems, so they don't need to be discussed together. +This is further discussed in the rationale and alternatives and future possibilities sections.

+

User Experience

+

The scope of this functionality is limited to the overall verification result. +The rationale section discusses the granularity of failures, and how this attribute could be extended.

+

Single Harness

+

Let's look at this code:

+
struct Device {
+    is_init: bool,
+}
+
+impl Device {
+    fn new() -> Self {
+        Device { is_init: false }
+    }
+
+    fn init(&mut self) {
+        assert!(!self.is_init);
+        self.is_init = true;
+    }
+}
+
+#[kani::proof]
+fn cannot_init_device_twice() {
+    let mut device = Device::new();
+    device.init();
+    device.init();
+}
+
+

This is what a negative harness may look like. +The user wants to verify that calling device.init() more than once should result in a panic.

+
+

NOTE: We could convert this into a Rust unit test and add the #[should_panic] attribute to it. +However, there are a few good reasons to have a verification-specific attribute that does the same:

+
    +
  1. To ensure that other unexpected behaviors don't occur (e.g., overflows).
  2. +
  3. Because #[should_panic] cannot be used if the test harness contains calls to Kani's API.
  4. +
  5. To ensure that a panic still occurs after stubbing out code which is expected to panic.
  6. +
+
+

Currently, this example produces a VERIFICATION:- FAILED result. +In addition, it will return a non-successful code.

+
#[kani::proof]
+#[kani::should_panic]
+fn cannot_init_device_twice() {
+    let mut device = Device::new();
+    device.init();
+    device.init();
+}
+
+

Since we added #[kani::should_panic], running this example would produce a successful code.

+

Now, we've considered two ways to represent this result in the verification output. +Note that it's important that we provide the user with this feedback:

+
    +
  1. (Expectation) Was Kani expecting the harness to panic?
  2. +
  3. (Outcome): What's the actual result that Kani produced after the analysis? +This will avoid a potential scenario where the user doesn't know for sure if the attribute has had an effect when verifying the harness.
  4. +
+

Therefore, the representation must make clear both the expectation and the outcome. +Below, we show how we'll represent this result.

+ +

The #[kani::should_panic] attribute essentially behaves as a property that depends on other properties. +This makes it well-suited for integration within the framework of Global Conditions.

+

Using the #[kani::should_panic] attribute will enable the global condition with name should_panic. +Following the format for global conditions, the outcome will be one of the following:

+
    +
  1. - `should_panic`: FAILURE (encountered no panics, but at least one was expected) if there were no failures.
  2. +
  3. - `should_panic`: FAILURE (encountered failures other than panics, which were unexpected) if there were failures but not all them had prop.property_class() == "assertion".
  4. +
  5. - `should_panic`: SUCCESS (encountered one or more panics as expected) otherwise.
  6. +
+

Note that the criteria to achieve a SUCCESS status depends on all failed properties having the property class "assertion". +If they don't, then the failed properties may contain UB, so we return a FAILURE status instead.

+

Multiple Harnesses

+

When there are multiple harnesses, we'll implement the single-harness changes in addition to the following ones. +Currently, a "Summary" section appears1 after reporting the results for each harness:

+
Verification failed for - harness3
+Verification failed for - harness2
+Verification failed for - harness1
+Complete - 0 successfully verified harnesses, 3 failures, 3 total.
+
+

Harnesses marked with #[kani::should_panic] won't show unless the expected result was different from the actual result. +The summary will consider harnesses that match their expectation as "successfully verified harnesses".

+

Therefore, if we added #[kani::should_panic] to all harnesses in the previous example, we'd see this output:

+
Complete - 3 successfully verified harnesses, 0 failures, 3 total.
+
+

Multiple panics

+

In a verification context, an execution can branch into multiple executions that depend on a condition. +This may result in a situation where different panics are reachable, as in this example:

+
#[kani::proof]
+#[kani::should_panic]
+fn branch_panics() {
+    let b: bool = kani::any();
+
+    do_something();
+
+    if b {
+        call_panic_1(); // leads to a panic-related failure
+    } else {
+        call_panic_2(); // leads to a different panic-related failure
+    }
+}
+
+

Note that we could safeguard against these situations by checking that only one panic-related failure is reachable. +However, users have expressed that a coarse version (i.e., checking that at least one panic can be reached) is preferred. +Users also anticipate that #[kani::should_panic] will be used to exercise smoke testing in many cases. +Additionally, restricting #[kani::should_panic] to the verification of single panic-related failures could be confusing for users and reduce its overall usefulness.

+

Availability

+

This feature will only be available as an attribute. +That means this feature won't be available as a CLI option (i.e., --should-panic). +There are good reasons to avoid the CLI option:

+
    +
  • It'd make the design and implementation unnecessarily complex.
  • +
  • It'd only be useful when combined with --harness to filter negative harnesses.
  • +
  • We could have trouble extending its functionality (see Future possibilities for more details).
  • +
+

Pedagogy

+

The #[kani::should_panic] attribute will become one of the most basic attributes in Kani. +As such, it'll be mentioned in the tutorial and added to the dedicated section planned in #2208.

+

In general, we'll also advise against negative verification when a harness can be written both as a regular (positive) harness and a negative one. +The feature, as it's presented in this proposal, won't allow checking that the panic failure is due to the panic we expected. +So there could be cases where the panic changes, but it goes unnoticed while running Kani. +Because of that, it'll preferred that users write positive harnesses instead.

+

Detailed Design

+

At a high level, we expect modifications in the following components:

+
    +
  • kani-compiler: Changes required to (1) process the new attribute, and (2) extend HarnessMetadata with a should_panic: bool field.
  • +
  • kani-driver: Changes required to (1) edit information about harnesses printed by kani-driver, (2) edit verification output when post-processing CBMC verification results, and (3) return the appropriate exit status after post-processing CBMC verification results.
  • +
+

We don't expect these changes to require new dependencies. +Besides, we don't expect these changes to be updated unless we decide to extend the attribute with further fields (see Future possibilities for more details).

+

Rationale and alternatives

+

This proposal would enable users to exercise negative verification with a relatively simple mechanism. +Not adding such a mechanism could impact Kani's usability by limiting the harnesses that users can write.

+

Alternative #1: Generic failures

+

This proposal doesn't consider generic failures but only panics. +In principle, it's not clear that a mechanism for generic failures would be useful. +Such a mechanism would allow users to expect UB in their harness, but there isn't a clear motivation for doing that.

+

Alternative #2: Name

+

We have considered two alternatives for the "expectation" part of the attribute's name: should and expect. +We avoid expect altogether for two reasons:

+
    +
  • We may consider adding the expected argument to #[kani::should_panic].
  • +
  • We may consider a more granular approach to indicate expectations regarding individual checks and cover statements in the future. One possible name for the attribute is #[kani::expect].
  • +
  • We heavily use this word for testing in Kani: there is an expected mode, which works with *.expected files. Other modes also use such files.
  • +
+

Alternative #3: The expected argument

+

We could consider an expected argument, similar to the #[should_panic] attribute. +To be clear, the #[should_panic] attribute may receive an argument expected which allows users to specify the expected panic string:

+
    #[test]
+    #[should_panic(expected = "Divide result is zero")]
+    fn test_specific_panic() {
+        divide_non_zero_result(1, 10);
+    }
+
+

In principle, we anticipate that we'll extend this proposal to include the expected argument at some point. +The implementation could compare the expected string against the panic string.

+

At present, the only technical limitation is that panic strings printed in Kani aren't formatted. +One option is to use substrings to compare. +However, the long-term solution is to use concrete playback to replay the panic and match against the expected panic string. +By doing this, we would achieve feature parity with Rust's #[should_panic].

+

Alternative #4: Granularity

+

As mentioned earlier, users may want to express more granular expectations for their harnesses.

+

There could be problems with this proposal if we attempt to do both:

+
    +
  • What if users don't want to only check for failures (e.g., reachability)?
  • +
  • In the previous case, would they expect the overall verification to fail or not?
  • +
  • How do we want these expectations to be declared?
  • +
+

We don't have sufficient data about the use-case considered in this alternative. +This proposal can also contribute to collect this data: once users can expect panics, they may want to expect other things.

+

Alternative #5: Kani API

+

This functionality could be part of the Kani API instead of being an attribute. +For example, some contributors proposed a function that takes a predicate closure to filter executions and check that they result in a panic.

+

However, such a function couldn't be used in external code, limiting its usability to the user's code.

+

Open questions

+

Once the feature is available, it'd be good to gather user feedback to answer these questions:

+
    +
  • Do we need a mechanism to express more granular expectations?
  • +
  • If we need the mechanism in (2), do we really want to collapse them into one feature?
  • +
+

Resolved questions

+
    +
  • What is the best representation to use for this feature? A representation that changes the overall result seems to be preferred, according to feedback we received during a discussion.
  • +
  • Do we want to extend #[kani::should_panic] with an expected field? Yes, but not in this version.
  • +
  • Do we want to allow multiple panic-related failures with #[kani::should_panic]? Yes (this is now discussed in User Experience).
  • +
+

Future possibilities

+
    +
  • The attribute could be an argument to kani::proof (#[kani::proof(should_panic)] reads very well).
  • +
  • Add an expected argument to #[kani::should_panic], and replay the harness with concrete playback to get the actual panic string.
  • +
+
2 +

Double negation may not be the best representation, but it's at least accurate with respect to the original result.

+
+
1 +

This summary is printed in both the default and terse outputs.

+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0006-unstable-api.html b/rfc/rfcs/0006-unstable-api.html new file mode 100644 index 000000000000..6ca3d8a5fa7b --- /dev/null +++ b/rfc/rfcs/0006-unstable-api.html @@ -0,0 +1,271 @@ + + + + + + 0006-unstable-api - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+

Summary

+

Provide a standard option for users to enable experimental APIs and features in Kani, +and ensure that those APIs are off by default.

+

User Impact

+

Add an opt-in model for users to try experimental APIs. +The goal is to enable users to try features that aren't stable yet, +which allow us to get valuable feedback during the development of new features and APIs.

+

The opt-in model empowers the users to control when some instability is acceptable, +which makes Kani UX more consistent and safe.

+

Currently, each new unstable feature will introduce a new switch, some of them will look like --enable-<feature>, +while others will be a plain switch which allows further feature configuration --<feature-config>=[value]. +For example, we today have the following unstable switches --enable-stubbing, --concrete-playback, --gen-c. +In all cases, users are still required to provide the additional --enable-unstable option. +Some unstable features are included in the --help section, and only a few mention the requirement +to include --enable-unstable. There is no way to list all unstable features. +The transition to stable switches is also ad-hoc.

+

In order to reduce friction, we will also standardize how users opt-in to any Kani unstable feature. +We will use similar syntax to the one used by the Rust compiler and Cargo. +As part of this work, we will also deprecate and remove --enable-unstable option.

+

Note that although Kani is still on v0, which means that everything is somewhat unstable, +this allow us to set different bars when it comes to what kind of changes is expected, +as well as what kind of support we will provide for a feature.

+

User Experience

+

Users will have to invoke Kani with:

+
-Z <feature_identifier>
+
+

in order to enable any unstable feature in Kani, including unstable APIs in the Kani library. +For unstable command line options, we will add -Z unstable-options, similar to the Rust compiler. +E.g.:

+
-Z unstable-options --concrete-playback=print
+
+

Users will also be able to enable unstable features in their Cargo.toml in the unstable table +under kani table. E.g:

+
[package.metadata.kani.unstable]
+unstable-options = true
+
+[workspace.metadata.kani]
+flags = { concrete-playback = true }
+unstable = { unstable-options = true }
+
+

In order to mark an API as unstable, we will add the following attribute to the APIs marked as unstable:

+
#[kani::unstable(feature="<IDENTIFIER>", issue="<TRACKING_ISSUE_NUMBER>", reason="<DESCRIPTION>")]
+pub fn unstable_api() {}
+
+

This is similar to the interface used by the standard library.

+

If the user tries to use an unstable feature in Kani without explicitly enabling it, +Kani will trigger an error. For unstable APIs, the error will be triggered during the crate +compilation.

+

Detailed Design

+

We will add the -Z option to both kani-driver and kani-compiler. +Kani driver will pass the information to the compiler.

+

For unstable APIs, the compiler will check if any reachable function uses an unstable feature that was not enabled. +If that is the case, the compiler will trigger a compilation error.

+

We will also change the compiler to only generate code for harnesses that match the harness filter. +The filter is already passed to the compiler, but it is currently only used for stubbing.

+

API Stabilization

+

Once an API has been stabilized, we will remove the unstable attributes from the given API. +If the user tries to enable a feature that was already stabilized, +Kani will print a warning stating that the feature has been stabilized.

+

API Removal

+

If we decide to remove an API that is marked as unstable, we should follow a regular deprecation +path (using #[deprecated] attribute), and keep the unstable flag + attributes, until we are +ready to remove the feature completely.

+

Rational and Alternatives

+

For this RFC, the suggestion is to only enable experimental features globally for simplicity of use and implementation.

+

For now, we will trigger a compilation error if an unstable API is reachable from a user crate +unless if the user opts in for the unstable feature.

+

We could allow users to specify experimental features on a per-harness basis, +but it could be tricky to make it clear to the user which harness may be affected by which feature. +The extra granularity would also be painful when we decide a feature is no longer experimental, +whether it is stabilized or removed. +In those cases, users would have to edit each harness that enables the affected feature.

+

Open questions

+
    +
  • Should we also add a stable attribute that documents when an API was stabilized?
  • +
+

Future possibilities

+
    +
  • Delay the error due to the usage of a unstable API, and only fail at runtime if the API is reachable.
  • +
  • Allow users to enable unstable features on a per-harness basis.
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0007-global-conditions.html b/rfc/rfcs/0007-global-conditions.html new file mode 100644 index 000000000000..1c48ce022b43 --- /dev/null +++ b/rfc/rfcs/0007-global-conditions.html @@ -0,0 +1,266 @@ + + + + + + 0007-global-conditions - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+

Summary

+

A new section in Kani's output to summarize the status of properties that depend on other properties. We use the term global conditions to refer to such properties.

+

User Impact

+

The addition of new options that affect the overall verification result depending on certain property attributes demands some consideration. +In particular, the addition of a new option to fail verification if there are uncoverable (i.e., unsatisfiable or unreachable) cover properties (requested in #2299) is posing new challenges to our current architecture and UI.

+

This concept isn't made explicit in Kani, but exists in some ways. +For example, the kani::should_panic attribute is a global condition because it can be described in terms of other properties (checks). +The request in #2299 is essentially another global conditions, and we may expect more to be requested in the future.

+

In this RFC, we propose a new section in Kani's output focused on reporting global conditions. +The goal is for users to receive useful information about hyperproperties without it becoming overwhelming. +This will help users to understand better options that are enabled through global conditions and ease the addition of such options to Kani.

+

User Experience

+

The output will refer to properties that depend on other properties as "global conditions", which is a simpler term. +The options to enable different global conditions will depend on a case-by-case basis1.

+

The main UI change in this proposal is a new GLOBAL CONDITIONS section that won't be printed if no global conditions have been enabled. +This section will only appear in Kani's default output after the RESULTS section (used for individual checks) and have the format:

+
GLOBAL CONDITIONS:
+ - `<name>`: <status> (<reason>)
+ - `<name>`: <status> (<reason>)
+ [...]
+
+

where:

+
    +
  • <name> is the name given to the global condition.
  • +
  • <status> is the status determined for the global condition.
  • +
  • <reason> is an explanation that depends on the status of the global condition.
  • +
+

For example, let's assume we implement the option requested in #2299. +A concrete example of this output would be:

+
GLOBAL CONDITIONS:
+ - `fail_uncoverable`: SUCCESS (all cover statements were satisfied as expected)
+
+

A FAILED status in any enabled global condition will cause verification to fail. +In that case, the overall verification result will point out that one or more global conditions failed, as in:

+
VERIFICATION:- FAILURE (one or more global conditions failed)
+
+

This last UI change will also be implemented for the terse output. +Finally, checks that cause an enabled global condition to fail will be reported using the same interface we use for failed checks2.

+

Global conditions which aren't enabled won't appear in the GLOBAL CONDITIONS section. +Their status will be computed regardless3, and we may consider showing this status when the --verbose option is passed.

+

The documentation of global conditions will depend on how they're enabled, which depends on a case-by-case basis. +However, we may consider adding a new subsection Global conditions to the Reference section that collects all of them so it's easier for users to consult all of them in one place.

+

Detailed Design

+

The only component to be modified is kani-driver since that's where verification results are built and determined. +But we should consider moving this logic into another crate.

+

We don't need new dependencies. +The corner cases will depend on the specific global conditions to be implemented.

+

Rationale and alternatives

+

As mentioned earlier, we're proposing this change to help users understand global conditions and how they're determined. +In many cases, global conditions empower users to write harnesses which weren't possible to write before. +As an example, the #[kani::should_panic] attribute allowed users to write harnesses expecting panic-related failures.

+

Also, we don't really know if more global conditions will be requested in the future. +We may consider discarding this proposal and waiting for the next feature that can be implemented as a global condition to be requested.

+

Alternative: Global conditions as regular checks

+

One option we've considered in the past is to enable global conditions as a regular checks. +While it's technically doable, it doesn't feel appropriate for global conditions to reported through regular checks since generally a higher degree of visibility may be appreciated.

+

Open questions

+

No open questions.

+

Future possibilities

+

A redesign of Kani's output is likely to change the style/architecture to report global conditions.

+
3 +

The results for global conditions would be computed during postprocessing based on the results of other checks. +Global conditions' checks aren't part of the SAT, therefore this computation won't impact verification time.

+
+
2 +

We do not discuss the specific interface to report the failed checks because it needs improvements (for both global conditions and standard verification). +In particular, the description field is the only information printed for properties (such as cover statements) without trace locations. +There are additional improvements we should consider: printing the actual status (for global conditions, this won't always be FAILED), avoid the repetition of Failed Checks: , etc. +This comment discusses problems with the current interface on some examples.

+
+
1 +

In other words, global conditions won't force a specific mechanism to be enabled. +For example, if the #[kani::should_panic] attribute is converted into a global condition, it will continue to be enabled through the attribute itself. +Other global conditions may be enabled through CLI flags only (e.g., --fail-uncoverable), or a combination of options in general.

+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0008-line-coverage.html b/rfc/rfcs/0008-line-coverage.html new file mode 100644 index 000000000000..9cf2d5cc5cda --- /dev/null +++ b/rfc/rfcs/0008-line-coverage.html @@ -0,0 +1,325 @@ + + + + + + 0008-line-coverage - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+

Summary

+

Add verification-based line coverage reports to Kani.

+

User Impact

+

Nowadays, users can't easily obtain verification-based coverage reports in Kani. +Generally speaking, coverage reports show which parts of the code under verification are covered and which are not. +Because of that, coverage is often seen as a great metric to determine the quality of a verification effort.

+

Moreover, some users prefer using coverage information for harness development and debugging. +That's because coverage information provides users with more familiar way to interpret verification results.

+

This RFC proposes adding a new option for verification-based line coverage reports to Kani. +As mentioned earlier, we expect users to employ this coverage-related option on several stages of a verification effort:

+
    +
  • Learning: New users are more familiar with coverage reports than property-based results.
  • +
  • Development: Some users prefer coverage results to property-based results since they are easier to interpret.
  • +
  • CI Integration: Users may want to enforce a minimum percentage of code coverage for new contributions.
  • +
  • Debugging: Users may find coverage reports particularly helpful when inputs are over-constrained (missing some corner cases).
  • +
  • Evaluation: Users can easily evaluate where and when more verification work is needed (some projects aim for 100% coverage).
  • +
+

Moreover, adding this option directly to Kani, instead of relying on another tools, is likely to:

+
    +
  1. Increase the speed of development
  2. +
  3. Improve testing for coverage features
  4. +
+

Which translates into faster and more reliable coverage options for users.

+

User Experience

+

The goal is for Kani to generate code coverage report per harness in a well established format, such as LCOV, and possibly a summary in the output. +For now, we will focus on an interim solution that will enable us to assess the results of our instrumentation and enable integration with the Kani VS Code extension.

+

High-level changes

+

For the first version, this experimental feature will report verification results along coverage reports. +Because of that, we'll add a new section Coverage results that shows coverage results for each individual harness.

+

In the following, we describe an experimental output format. +Note that the final output format and overall UX is to be determined.

+

Experimental output format for coverage results

+

The Coverage results section for each harness will produce coverage information in a CSV format as follows:

+
<file>, <line>, <status>
+
+

where <status> is either FULL, PARTIAL or NONE.

+

As mentioned, this format is designed for evaluating the native instrumentation-based design and is likely to be substituted with another well-established format as soon as possible.

+

Users are not expected to consume this output directly. +Instead, coverage data is to be consumed by the Kani VS Code extension and displayed as in the VS Code Extension prototype.

+

How to activate and display coverage information in the extension is out of scope for this RFC. +That said, a proof-of-concept implementation is available here.

+

Detailed Design

+

Architecture

+

We will add a new unstable --coverage verification option to Kani which will require -Z line-coverage until this feature is stabilized. +We will also add a new --coverage-checks option to kani-compiler, which will result in the injection of coverage checks before each Rust statement and terminator1. +This option will be supplied by kani-driver when the --coverage option is selected. +These options will cause Kani to inject coverage checks during compilation and postprocess them to produce the coverage results sections described earlier.

+

Coverage Checks

+

Coverage checks are a new class of checks similar to cover checks. +The main difference is that users cannot directly interact with coverage checks (i.e., they cannot add or remove them manually). +Coverage checks are encoded as an assert(false) statement (to test reachability) with a fixed description. +In addition, coverage checks are:

+
    +
  • Hidden from verification results.
  • +
  • Postprocessed to produce coverage results.
  • +
+

In the following, we describe the injection and postprocessing procedures to generate coverage results.

+

Injection of Coverage Checks

+

The injection of coverage checks will be done while generating code for basic blocks. +This allows us to add one coverage check before each statement and terminator, which provides the most accurate results1. +It's not completely clear how this compares to the coverage instrumentation done in the Rust compiler, but an exploration to use the compiler APIs revealed that they're quite similar2.

+

Postprocessing Coverage Checks

+

The injection of coverage checks often results in one or more checks per line (assuming a well-formatted program). +We'll postprocess these checks so for each line

+
    +
  • if all checks are SATISFIED: return FULL
  • +
  • if all checks are UNSATISFIED: return NONE
  • +
  • otherwise: return PARTIAL
  • +
+

We won't report coverage status for lines which don't include a coverage check.

+

Rationale and alternatives

+

Benefits from a native coverage solution

+

Kani has relied on cbmc-viewer to report coverage information since the beginning. +In essence, cbmc-viewer consumes data from coverage-focused invocations of CBMC and produces an HTML report containing (1) coverage information and (2) counterexample traces. +Recently, there have been some issues with the coverage information reported by cbmc-viewer (e.g., #2048 or #1707), forcing us to mark the --visualize option as unstable and disable coverage results in the reports (in #2206).

+

However, it's possible for Kani to report coverage information without cbmc-viewer, as explained before. +This would give Kani control on both ends:

+
    +
  • The instrumentation performed on the program. Eventually, this would allow us to report more precise coverage information (maybe similar to Rust's instrument-based code coverage).
  • +
  • The format of the coverage report to be generated. Similarly, this would allow us to generate coverage data in different formats (see #1706 for GCOV, or #1777 for LCOV). While technically this is also doable from cbmc-viewer's output, development is likely to be faster this way.
  • +
+

Coverage through cbmc-viewer

+

As an alternative, we could fix and use cbmc-viewer to report line coverage.

+

Most of the issues with cbmc-viewer are generally due to:

+
    +
  1. Missing locations due to non-propagation of locations in either Kani or CBMC.
  2. +
  3. Differences in the definition of a basic block in CBMC and Rust's MIR.
  4. +
  5. Scarce documentation for coverage-related options (i.e., --cover <option>) in CBMC.
  6. +
  7. Limited testing with Rust code in cbmc-viewer.
  8. +
+

Note that (1) is not exclusive to coverage results from cbmc-viewer. +Finding checks with missing locations and propagating them if possible (as suggested in this comment) should be done regardless of the approach used for line coverage reports.

+

In contrast, (2) and (3) can be considered the main problems for Kani contributors to develop coverage options on top of cbmc-viewer and CBMC. +It's not clear how much effort this would involve, but (3) is likely to require substantial documentation contributions. +But (4) shouldn't be an issue if we decided to invest in cbmc-viewer.

+

Finally, the following downside must be considered: +cbmc-viewer can report line coverage but the path to report region-based coverage may involve a complete rewrite.

+

Other output formats

+

One of the long-term goals for this feature is to provide a UX that is familiar for users. +This is particularly relevant when talking about output formats. +Some services and frameworks working with certain coverage output formats have become quite popular.

+

However, this version doesn't consider common output formats (i.e., GCOV or LCOV) since coverage results will only be consumed by the Kani VS Code Extension at first. +But other output formats will be considered in the future.

+

Open questions

+

Open questions:

+
    +
  • Do we want to report line coverage as COVERED/UNCOVERED or FULL/PARTIAL/NONE?
  • +
  • Should we report coverage results and verification results or not? Doing both is likely to result in worse performance. We have to perform an experimental evaluation with hard benchmarks.
  • +
  • Should we instrument dependencies or not? Doing so is likely to result in worse performance. We have to perform an experimental evaluation.
  • +
  • What should be the final UX for this feature? For instance, we could print a coverage summary and generate a report file per harness. But it's not clear if individual results are relevant to users, so another possibility is to automatically combine results.
  • +
  • What's the most appropriate and well-established output format we can emit?
  • +
  • Determine if there are cases in which coverage information is confusing for users (due to, e.g., constant propagation or other compiler optimizations). How can work around such cases?
  • +
  • Do we want to report coverage information for dependencies? For CI, most users may be only interested in their code. Most coverage frameworks have an aggregation tool with an option to exclude dependencies from coverage metrics.
  • +
+

Feedback to gather before stabilization:

+ +

Future possibilities

+

We expect many incremental improvements in the coverage area:

+
    +
  1. Consuming the output produced in coverage results from the Kani VS Code extension.
  2. +
  3. Building a tool that produces coverage results by combining the coverage results of more than one harness.
  4. +
  5. Including span information in coverage checks and building region-based coverage reports.
  6. +
  7. Adding new user-requested coverage formats such as GCOV #1706 or LCOV #1777.
  8. +
+
1 +

We have experimented with different options for injecting coverage checks. +For example, we have tried injecting one before each basic block, or one before each statement, etc. +The proposed option (one before each statement AND each terminator) gives us the most accurate results.

+
+
2 +

In particular, comments in CoverageSpan and generate_coverage_spans hint that the initial set of spans come from Statements and Terminators. This comment goes in detail about the attempt to use the compiler APIs.

+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0009-function-contracts.html b/rfc/rfcs/0009-function-contracts.html new file mode 100644 index 000000000000..7a723a183a47 --- /dev/null +++ b/rfc/rfcs/0009-function-contracts.html @@ -0,0 +1,1004 @@ + + + + + + 0009-function-contracts - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+
    +
  • Feature Name: Function Contracts
  • +
  • Feature Request Issue: #2652 and Milestone
  • +
  • RFC PR: #2620
  • +
  • Status: Unstable
  • +
  • Version: 1
  • +
  • Proof-of-concept: features/contracts
  • +
  • Feature Gate: -Zfunction-contracts, enforced by compile time error1
  • +
+
+

Summary

+

Function contracts are a means to specify and check function behavior. On top of +that the specification can then be used as a sound2 +abstraction to replace the concrete implementation, similar to stubbing.

+

This allows for a modular verification.

+ +

User Impact

+ +

Function contracts provide an interface for a verified, +sound2 function abstraction. This is similar to stubbing +but with verification of the abstraction instead of blind trust. This allows for +modular verification, which paves the way for the following two ambitious goals.

+
    +
  • Scalability: A function contract is an abstraction (sound +overapproximation) of a function's behavior. After verifying the contract +against its implementation we can subsequently use the (cheaper) abstraction +instead of the concrete implementation when analyzing its callers. +Verification is thus modularized and even cacheable.
  • +
  • Unbounded Verification: Contracts enable inductive reasoning for recursive +functions where the first call is checked against the contract and recursive +calls are stubbed out using the abstraction.
  • +
+

Function contracts are completely optional with no user impact if unused. This +RFC proposes the addition of new attributes, and functions, that shouldn't +interfere with existing functionalities.

+

User Experience

+

A function contract specifies the behavior of a function as a predicate that +can be checked against the function implementation and also used as an +abstraction of the implementation at the call sites.

+

The lifecycle of a contract is split into three phases: specification, +verification and call abstraction, which we will explore on this example:

+
fn my_div(dividend: u32, divisor: u32) -> u32 {
+  dividend / divisor
+}
+
+
    +
  1. +

    In the first phase we specify the contract. Kani provides two new +annotations: requires (preconditions) to describe the expectations this +function has as to the calling context and ensures (postconditions) which +approximates function outputs in terms of function inputs.

    +
    #[kani::requires(divisor != 0)]
    +#[kani::ensures(|result : &u32| *result <= dividend)]
    +fn my_div(dividend: u32, divisor: u32) -> u32 {
    +  dividend / divisor
    +}
    +
    +

    requires here indicates this function expects its divisor input to never +be 0, or it will not execute correctly (for instance panic or cause undefined +behavior).

    +

    ensures puts a bound on the output, relative to the dividend input.

    +

    Conditions in contracts are Rust expressions which reference the +function arguments and, in case of ensures, the return value of the +function. The return value is passed into the ensures closure statement by reference. Syntactically +Kani supports any Rust expression, including function calls, defining types +etc. However they must be side-effect free (see also side effects +here) or Kani will throw a compile error.

    +

    Multiple requires and ensures clauses are allowed on the same function, +they are implicitly logically conjoined.

    +
  2. +
  3. +

    Next, Kani ensures that the function implementation respects all the conditions specified in its contract.

    +

    To perform this check Kani needs a suitable harness to verify the function +in. The harness is mainly responsible for providing the function arguments +but also set up a valid heap that pointers may refer to and properly +initialize static variables.

    +

    Kani demands of us, as the user, to provide this harness; a limitation of +this proposal. See also future possibilities for a +discussion about the arising soundness issues and their remedies.

    +

    Harnesses for checking contract are defined with the +proof_for_contract(TARGET) attribute which references TARGET, the +function for which the contract is supposed to be checked.

    +
    #[kani::proof_for_contract(my_div)]
    +fn my_div_harness() {
    +  my_div(kani::any(), kani::any())
    +}
    +
    +

    Similar to a verification harness for any other function, we are supposed to +create all possible input combinations the function can encounter, then call +the function at least once with those abstract inputs. If we forget to call +my_div Kani reports an error. Unlike other harnesses we only need to create +suitable data structures but we don't need to add any checks as Kani will +use the conditions we specified in the contract.

    +

    Kani inserts preconditions (requires) as kani::assume before the call +to my_div, limiting inputs to those the function is actually defined for. +It inserts postconditions (ensures) as kani::assert checks after the +call to my_div, enforcing the contract.

    +

    The expanded version of our harness that Kani generates looks roughly like +this:

    +
    #[kani::proof]
    +fn my_div_harness() {
    +  let dividend = kani::any();
    +  let divisor = kani::any();
    +  kani::assume(divisor != 0); // requires
    +  let result_kani_internal = my_div(dividend, divisor);
    +  kani::assert((|result : &u32| *result <= dividend)(result_kani_internal)); // ensures
    +}
    +
    +

    Kani verifies the expanded harness like any other harness, giving the +green light for the next step: call abstraction.

    +
  4. +
  5. +

    In the last phase the verified contract is ready for us to use to +abstract the function at its call sites.

    +

    Kani requires that there has to be at least one associated +proof_for_contract harness for each abstracted function, otherwise an error is +thrown. In addition, by default, it requires all proof_for_contract +harnesses to pass verification before attempting verification of any +harnesses that use the contract as a stub.

    +

    A possible harness that uses our my_div contract could be the following:

    +
    #[kani::proof]
    +#[kani::stub_verified(my_div)]
    +fn use_div() {
    +  let v = vec![...];
    +  let some_idx = my_div(v.len() - 1, 3);
    +  v[some_idx];
    +}
    +
    +

    At a call site where the contract is used as an abstraction Kani +kani::asserts the preconditions (requires) and produces a +nondeterministic value (kani::any) which satisfies the postconditions.

    +

    Mutable memory is similarly made non-deterministic, discussed later in +havocking.

    +

    An expanded stubbing of my_div looks like this:

    +
    fn my_div_stub(dividend: u32, divisor: u32) -> u32 {
    +  kani::assert(divisor != 0); // pre-condition
    +  kani::any_where(|result| { /* post-condition */ result <= dividend })
    +}
    +
    +

    Notice that this performs no actual computation for my_div (other than the +conditions) which allows us to avoid something potentially costly.

    +
  6. +
+

Also notice that Kani was able to express both contract checking and abstracting +with existing capabilities; the important feature is the enforcement. The +checking is, by construction, performed against the same condition that is +later used as the abstraction, which ensures soundness (see discussion on +lingering threats to soundness in the future section) +and guarding against abstractions diverging from their checks.

+

Write Sets and Havocking

+

Functions can have side effects on data reachable through mutable references or +pointers. To overapproximate all such modifications a function could apply to +pointed-to data, the verifier "havocs" those regions, essentially replacing +their content with non-deterministic values.

+

Let us consider a simple example of a pop method.

+
impl<T> Vec<T> {
+  fn pop(&mut self) -> Option<T> {
+    ...
+  }
+}
+
+

This function can, in theory, modify any memory behind &mut self, so this is +what Kani will assume it does by default. It infers the "write set", that is the +set of memory locations a function may modify, from the type of the function +arguments. As a result, any data pointed to by a mutable reference or pointer is +considered part of the write set3. In addition, a static +analysis of the source code discovers any static mut variables the function or +it's dependencies reference and adds all pointed-to data to the write set also.

+

During havocking the verifier replaces all locations in the write set with +non-deterministic values. Kani emits a set of automatically generated +postconditions which encode the expectations from the Rust type system and +assumes them for the havocked locations to ensure they are valid. This +encompasses both limits as to what values are acceptable for a given type, such +as char or the possible values of an enum discriminator, as well as lifetime +constraints.

+

While the inferred write set is sound and enough for successful contract +checking4 in many cases this inference is too coarse +grained. In the case of pop every value in this vector will be made +non-deterministic.

+

To address this the proposal also adds a modifies and frees clause which +limits the scope of havocking. Both clauses represent an assertion that the +function will modify only the specified memory regions. Similar to +requires/ensures the verifier enforces the assertion in the checking stage to +ensure soundness. When the contract is used as an abstraction, the modifies +clause is used as the write set to havoc.

+

In our pop example the only modified memory location is the last element and +only if the vector was not already empty, which would be specified thusly.

+
impl<T> Vec<T> {
+  #[modifies(if !self.is_empty() => (*self).buf.ptr.pointer.pointer[self.len])]
+  #[modifies(if self.is_empty())]
+  fn pop(&mut self) -> Option<T> {
+    ...
+  }
+}
+
+

The #[modifies(when = CONDITION, targets = { MODIFIES_RANGE, ... })] consists +of a CONDITION and zero or more, comma separated MODIFIES_RANGEs which are +essentially a place expression.

+

Place expressions describe a position in the abstract program memory. You may +think of it as what goes to the left of an assignment. They compose of the name +of one function argument (or static variable) and zero or more projections +(dereference *, field access .x and slice indexing [1]5).

+

If no when is provided the condition defaults to true, meaning the modifies +ranges apply to all invocations of the function. If targets is omitted it +defaults to {}, e.g. an empty set of targets meaning under this condition the +function modifies no mutable memory.

+

Because place expressions are restricted to using projections only, Kani must +break Rusts pub/no-pub encapsulation here6. +If need be we can reference fields that are usually hidden, without an error +from the compiler.

+

In addition to a place expression, a MODIFIES_RANGE can also be terminated +with more complex slice expressions as the last projection. This only applies +to *mut pointers to arrays. For instance this is needed for Vec::truncate +where all of the latter section of the allocation is assigned (dropped).

+
impl<T> Vec<T> {
+  #[modifies(self.buf.ptr.pointer.pointer[len..])]
+  fn truncate(&mut self, len: usize) {
+    ...
+  }
+}
+
+

[..] denotes the entirety of an allocation, [i..], [..j] and [i..j] are +ranges of pointer offsets5. The slice indices are offsets with sizing T, e.g. +in Rust p[i..j] would be equivalent to +std::slice::from_raw_parts(p.offset(i), i - j). i must be smaller or equal +than j.

+

A #[frees(when = CONDITION, targets = { PLACE, ... })] clause works similarly +to modifies but denotes memory that is deallocated. Like modifies it applies +only to pointers but unlike modifies it does not admit slice syntax, only +place expressions, because the whole allocation has to be freed.

+

History Expressions

+

Kani's contract language contains additional support to reason about changes of +mutable memory. One case where this is necessary is whenever ensures needs to +refer to state before the function call. By default variables in the ensures +clause are interpreted in the post-call state whereas history expressions are +interpreted in the pre-call state.

+

Returning to our pop function from before we may wish to describe in which +case the result is Some. However that depends on whether self is empty +before pop is called. To do this Kani provides the old(EXPR) pseudo +function (see this section about a discussion on naming), +which evaluates EXPR before the call (e.g. to pop) and makes the result +available to ensures. It is used like so:

+
impl<T> Vec<T> {
+  #[kani::ensures(|result : &Option<T>| old(self.is_empty()) || result.is_some())]
+  fn pop(&mut self) -> Option<T> {
+    ...
+  }
+}
+
+

old allows evaluating any Rust expression in the pre-call context, so long as +it is free of side-effects. See also this +explanation. The borrow checker enforces that the +mutations performed by e.g. pop cannot be observed by the history expression, as +that would defeat the purpose. If you wish to return borrowed content from +old, make a copy instead (using e.g. clone()).

+

Note also that old is syntax, not a function and implemented as an extraction +and lifting during code generation. It can reference e.g. pop's arguments but +not local variables. Compare the following

+

Invalid ❌: #[kani::ensures(|result : &Option<T>| { let x = self.is_empty(); old(x) } || result.is_some())]
+Valid ✅: #[kani::ensures(|result : &Option<T>| old({ let x = self.is_empty(); x }) || result.is_some())]

+

And it will only be recognized as old(...), not as let old1 = old; old1(...) etc.

+

Workflow and Attribute Constraints Overview

+
    +
  1. By default kani or cargo kani first verifies all contract harnesses +(proof_for_contract) reachable from the file or in the local workspace +respectively.
  2. +
  3. Each contract (from the local +crate7) that is used in a +stub_verified is required to have at least one associated contract harness. +Kani reports any missing contract harnesses as errors.
  4. +
  5. Kani verifies all regular harnesses if their stub_verified contracts +passed step 1 and 2.
  6. +
+

When specific harnesses are selected (with --harness) contracts are not +verified.

+

Kani reports a compile time error if any of the following constraints are violated:

+
    +
  • +

    A function may have any number of requires, ensures, modifies and frees +attributes. Any function with at least one such annotation is considered as +"having a contract".

    +

    Harnesses (general or for contract checking) may not have any such annotation.

    +
  • +
  • +

    A harness may have up to one proof_for_contract(TARGET) annotation where TARGET must +"have a contract". One or more proof_for_contract harnesses may have the +same TARGET.

    +

    A proof_for_contract harness may use any harness attributes, including +stub and stub_verified, though the TARGET may not appear in either.

    +
  • +
  • +

    Kani checks that TARGET is reachable from the proof_for_contract harness, +but it does not warn if abstracted functions use TARGET8.

    +
  • +
  • +

    A proof_for_contract function may not have the kani::proof attribute (it +is already implied by proof_for_contract).

    +
  • +
  • +

    A harness may have multiple stub_verified(TARGET) attributes. Each TARGET +must "have a contract". No TARGET may appear twice. Each local TARGET is +expected to have at least one associated proof_for_contract harness which +passes verification, see also the discussion on when to check contracts in +open questions.

    +
  • +
  • +

    Harnesses may combine stub(S_TARGET, ..) and stub_verified(V_TARGET) +annotations, though no target may occur in S_TARGETs and V_TARGETs +simultaneously.

    +
  • +
  • +

    For mutually recursive functions using stub_verified, Kani will check their +contracts in non-deterministic order and assume each time the respective other +check succeeded.

    +
  • +
+

Detailed Design

+ +

Kani implements the functionality of function contracts in three places.

+
    +
  1. Code generation in the requires and ensures macros (kani_macros).
  2. +
  3. GOTO level contracts using CBMC's contract language generated in +kani-compiler for modifies clauses.
  4. +
  5. Dependencies and ordering among harnesses in kani-driver to enforce +contract checking before replacement. Also plumbing between compiler and +driver for enforcement of assigns clauses.
  6. +
+

Code generation in kani_macros

+

The requires and ensures macros perform code generation in the macro, +creating a check and a replace function which use assert and assume as +described in the user experience section. Both are attached +to the function they are checking/replacing by kanitool::checked_with and +kanitool::replaced_with attributes respectively. See also the +discussion about why we decided to generate check +and replace functions like this.

+

The code generation in the macros is straightforward, save two aspects: old +and the borrow checker.

+

The special old builtin function is implemented as an AST rewrite. Consider +the below example:

+
impl<T> Vec<T> {
+  #[kani::ensures(|result : &Option<T>| self.is_empty() || self.len() == old(self.len()) - 1)]
+  fn pop(&mut self) -> Option<T> {
+    ...
+  }
+}
+
+

The ensures macro performs an AST rewrite consisting of an extraction of the +expressions in old and a replacement with a fresh local variable, creating the +following:

+
impl<T> Vec<T> {
+  fn check_pop(&mut self) -> Option<T> {
+    let old_1 = self.len();
+    let result_kani_internal = Self::pop(self);
+    kani::assert((|result : &Option<T>| self.is_empty() || self.len() == old_1 - 1)(result_kani_internal))
+  }
+}
+
+

Nested invocations of old are prohibited (Kani throws an error) and the +expression inside may only refer to the function arguments and not other local +variables in the contract (Rust will report those variables as not being in +scope).

+

The borrow checker also ensures for us that none of the temporary variables +borrow in a way that would be able to observe the modification in pop which +would occur for instance if the user wrote old(self). Instead of borrowing +copies should be created (e.g. old(self.clone())). This is only enforced for +safe Rust though.

+

The second part relevant for the implementation is how we deal with the borrow +checker for postconditions. They reference the arguments of the function after +the call which is problematic if part of an input is borrowed mutably in the +return value. For instance the Vec::split_at_mut function does this and a +sensible contract for it might look as follows:

+
impl<T> Vec<T> {
+  #[ensures(|result : &(&mut [T], &mut [T])| self.len() == result.0.len() + result.1.len())]
+  fn split_at_mut(&mut self, i: usize) -> (&mut [T], &mut [T]) {
+    ...
+  }
+}
+
+

This contract refers simultaneously to self and the result. Since the method +however borrows self mutably, it would no longer be accessible in the +postcondition. To work around this we strategically break the borrowing rules +using a new hidden builtin kani::unchecked_deref with the type signature for <T> fn (&T) -> T which is essentially a C-style dereference operation. Breaking +the borrow checker like this is safe for 2 reasons:

+
    +
  1. Postconditions are not allowed perform mutation and
  2. +
  3. Post conditions are of type bool, meaning they cannot leak references to +the arguments and cause the race conditions the Rust type system tries to +prevent.
  4. +
+

The "copies" of arguments created by unsafe_deref are stored as fresh local +variables and their occurrence in the postcondition is renamed. In addition a +mem::forget is emitted for each copy to avoid a double free.

+

Recursion

+

Kani verifies contracts for recursive functions inductively. Reentry of the +function is detected with a function-specific static variable. Upon detecting +reentry we use the replacement of the contract instead of the function body.

+

Kani generates an additional wrapper around the function to add the detection. +The additional wrapper is there so we can place the modifies contract on +check_pop and replace_pop instead of recursion_wrapper which prevents CBMC +from triggering its recursion induction as this would skip our replacement checks.

+
#[checked_with = "recursion_wrapper"]
+#[replaced_with = "replace_pop"]
+fn pop(&mut self) { ... }
+
+fn check_pop(&mut self) { ... }
+
+fn replace_pop(&mut self) { ... }
+
+fn recursion_wrapper(&mut self) { 
+  static mut IS_ENTERED: bool = false;
+
+  if unsafe { IS_ENTERED } {
+    replace_pop(self)
+  } else {
+    unsafe { IS_ENTERED = true; }
+    let result = check_pop(self);
+    unsafe { IS_ENTERED = false; }
+    result
+  };
+}
+
+

Note that this is insufficient to verify all types of recursive functions, as +the contract specification language has no support for inductive lemmas (for +instance in ACSL section 2.6.3 +"inductive predicates"). Inductive lemmas are usually needed for recursive +data structures.

+

Changes to Other Components

+

Contract enforcement and replacement (kani::proof_for_contract(f), +kani::stub_verified(f)) both dispatch to the stubbing logic, stubbing f +with the generated check and replace function respectively. If f has no +contract, Kani throws an error.

+

For write sets Kani relies on CBMC. modifies clauses (whether derived from +types or from explicit clauses) are emitted from the compiler as GOTO contracts +in the artifact. Then the driver invokes goto-instrument with the name of the +GOTO-level function names to enforce or replace the memory contracts. The +compiler communicates the names of the function via harness metadata.

+

Code used in contracts is required to be side effect free which means it +must not perform I/O, mutate memory (&mut vars and such) or (de)allocate heap +memory. This is enforced in two layers. First with an MIR traversal over all +code reachable from a contract expression. An error is thrown if known +side-effecting actions are performed such as ptr::write, malloc, free or +functions which we cannot check, such as e.g. extern "C", with the exception +of known side effect free functions in e.g. the standard library.

+ +

Rationale and alternatives

+ + +

We developed the old contract for history expressions via understanding it as a modality originating from Moggi 1991. +The old monad links the "language of the past" to the "language of the present". +Implementing the full generality of the monad is rather difficult, so we focus on a particular usage of the monad.

+

We have an external syntax representation which is what the user inputs. We then parse this and logically manipulate it as a monad, prefixing all the bind operations. We then output the final compiled macro output as Rust code.

+

In particular, if we have an ensures statement like

+
#[kani::ensures(old(*ptr)+1==*ptr)]
+
+

Then we comprehend this as syntax for the statement (not within Rust)

+
bind (*ptr : O(u32)) (|remember : u32| remember + 1 == *ptr)
+
+

Here, the O(u32) is taking the type of the past u32 and converting it into a type in the present O(u32) while the bind operation lets you use the value of the past u32 to express a type in the present bool.

+

This then gets compiled to (within Rust)

+
let remember = *ptr;
+let result = ...;
+kani::assert(remember + 1 == *ptr)
+
+

This means that the underlying principle of the monad is there, but external syntax appears to be less like a monad because otherwise it would be too difficult to implement, and the user most likely only cares about this particular construction of prefixing all the bind operations.

+

This construction requires that old expressions are closed with resprect to the input parameters. This is due to the lifting into the prefixed bind operations.

+

A major drawback is that eta expansion fails. If we eta expand a function f, it becomes |x|f(x). Note that eta expansions guarantee that the original f and the |x|f(x) are equivalent which makes a lot of sense since you’re just calling the same function. However, we have that old(y) is not equivalent to (|x|old(x))(y). y is a closed expression, so the first statement works. x is a bound variable, so it is an open expression, so compilation will fail.

+

The reason for this restriction is that the user will most likely only want to use this particular prefixed bind structure for their code, so exposing the concept of monads to the user level would only confuse the user. It is also simpler from an implementation perspective to limit the monad to this particular usage.

+

As for nested old, such as old(old(*ptr)+*ptr), it is reasonable to interpret this as syntax representing

+
bind (bind(*ptr)(|remember_1| remember_1 + *ptr)) (|remember_0| ...)
+
+

which compiles to

+
let remember_1 = *ptr;
+let remember_0 = remember_1 + *ptr;
+let result = ...;
+...
+
+

so the restriction is just a matter of there not being implementation support for this kind of statement rather than the theory itself. It is not particularly useful to implement this because we claim that there should be no effectful computation within the contracts, so you can substitute the remember_1 into the second line without worrying about the effects. Hence, we opt for simply restricting this behavior instead of implementing it. (Note: it can be implemented by changing denier.visit_expr_mut(e); into self.visit_expr_mut(e);)

+ +

Kani-side implementation vs CBMC

+

Instead of generating check and replace functions in Kani, we could use the contract instrumentation provided by CBMC.

+

We tried this earlier but came up short, because it is difficult to implement, +while supporting arbitrary Rust syntax. We exported the conditions into +functions so that Rust would do the parsing/type checking/lowering for us and +then call the lowered function in the CBMC contract.

+

The trouble is that CBMC's old is only supported directly in the contract, not +in functions called from the contract. This means we either need to inline the +contract function body, which is brittle in the presence of control flow, or we +must extract the old expressions, evaluate them in the contract directly and +pass the results to the check function. However this means we must restrict the +expressions in old, because we now need to lower those by hand and even if we +could let rustc do it, CBMC's old has no support for function calls in its +argument expression.

+

Expanding all contract macros at the same time

+

Instead of expanding contract macros one-at-a-time and layering the checks we +could expand all subsequent one's with the outermost one in one go.

+

This is however brittle with respect to renaming. If a user does use kani::requires as my_requires and then does multiple +#[my_requires(condition)] macro would not collect them properly since it can +only match syntactically and it does not know about the use and neither can we +restrict this kind of use or warn the user. By contrast, the collection with +kanitool::checked_with is safe, because that attribute is generated by our +macro itself, so we can rely on the fact that it uses the canonical +representation.

+

Generating nested functions instead of siblings

+

Instead of generating the check and replace functions as siblings to the +contracted function we could nest them like so

+
fn my_div(dividend: u32, divisor: u32) -> u32 {
+  fn my_div_check_5e3713(dividend: u32, divisor: u32) -> u32 {
+    ...
+  }
+  ...
+}
+
+

This could be beneficial if we want to be able to allow contracts on trait impl +items, in which case generating sibling functions is not allowed. On the other +hand this makes it harder to implement contracts on trait definitions, +because there is no body available which we could nest the function into. +Ultimately we may require both so that we can support both.

+

What is required to make this work is an additional pass over the condition that +replaces every self with a fresh identifier that now becomes the first +argument of the function. In addition there are open questions as to how to +resolve the nested name inside the compiler.

+

Explicit command line checking/substitution vs attributes:

+

Instead of +adding a new special proof_for_contact attributes we could have instead done:

+
    +
  1. Check contracts on the command line like CBMC does. This makes contract +checking a separate kani invocation with something like a +--check-contract flag that directs the system to instrument the function. +This is a very flexible design, but also easily used incorrectly. +Specifically nothing in the source indicates which harnesses are supposed +to be used for which contract, users must remember to invoke the check and +are also responsible for ensuring they really do verify all contacts they +will later be replacing and lastly.
  2. +
  3. Check contracts with a #[kani::proof] harness. This would have used +e.g. a #[kani::for_contract] attributes on a #[kani::proof]. Since +#[kani::for_contract] is only valid on a proof, we decided to just +imply it and save the user some headache. Contract checking harnesses are +not meant to be reused for other purposes anyway and if the user really +wants to the can just factor out the actual contents of the harness to +reuse it.
  4. +
+

Polymorphism during contract checking

+

A current limitation with how contracts are enforced means that if the target of +a proof_for_contract is polymorphic, only one monomorphization is permitted to +occur in the harness. This does not limit the target to a single occurrence, +but to a single instantiation of its generic parameters.

+

This is because we rely on CBMC for enforcing the modifies contract. At the +GOTO level all monomorphized instances are distinct functions and CBMC only +allows checking one function contract at a time, hence this restriction.

+

User supplied harnesses

+

We make the user supply the harnesses for checking contracts. This is our major +source of unsoundness, if corner cases are not adequately covered. Having Kani +generate the harnesses automatically is a non-trivial task (because heaps are +hard) and will be the subject of future improvements.

+

In limited cases we could generate harnesses, for instance if only bounded types +(integers, booleans, enums, tuples, structs, references and their combinations) +were used. We could restrict the use of contracts to cases where only such types +are involved in the function inputs and outputs, however this would drastically +limit the applicability, as even simple heap data structures such as Vec, +String and even &[T] and &str (slices) would be out of scope. These data +structures however are ubiquitous and users can avoid the unsoundness with +relative confidence by overprovisioning (generating inputs that are several +times larger than what they expect the function will touch).

+

Open questions

+ +
    +
  • +

    Returning kani::any() in a replacement isn't great, because it wouldn't work +for references as they can't have an Arbitrary implementation. Plus the +soundness then relies on a correct implementation of Arbitrary. Instead it +may be better to allow for the user to specify type invariants which can the +be used to generate correct values in replacement but also be checked as part +of the contract checking.

    +
  • +
  • +

    Making result special. Should we use special syntax here like @result or +kani::result(), though with the latter I worry that people may get confused +because it is syntactic and not subject to usual use renaming and import +semantics. Alternatively we can let the user pick the name with an additional +argument to ensures, e.g. ensures(my_result_var, CONDITION)

    +

    Similar concerns apply to old, which may be more appropriate to be special +syntax, e.g. @old.

    +

    See #2597

    +
  • +
  • +

    How to check the right contracts at the right time. By default kani and +cargo kani check all contracts in a crate/workspace. This represents the +safest option for the user but may be too costly in some cases.

    +

    The user should be provided with options to disable contract checking for the +sake of efficiency. Such options may look like this:

    +
      +
    • By default (kani/cargo kani) all local contracts are checked, +harnesses are only checked if the contracts they depend on succeeded their check.
    • +
    • With harness selection (--harness) only those contracts which the +selected harnesses depend on are checked.
    • +
    • For high assurance passing a --paranoid flag also checks contracts for +dependencies (other crates) when they are used in abstractions.
    • +
    • Per harness the users can disable the checking for specific contracts +via attribute, like #[stub_verified(TARGET, trusted)] or +#[stub_unverified(TARGET)]. This also plays nicely with cfg_attr.
    • +
    • On the command line users can similarly disable contract checks by +passing (multiple times) --trusted TARGET to skip checking those +contracts.
    • +
    • The bold (or naïve) user can skip all contracts with --all-trusted.
    • +
    • For the lawyer that is only interested in checking contracts and nothing +else a --litigate flag checks only contract harnesses.
    • +
    +

    Aside: I'm obviously having some fun here with the names, happy to change, +it's really just about the semantics.

    +
  • +
  • +

    Can old accidentally break scope? The old function cannot reference local +variables. For instance #[ensures({let x = ...; old(x)})] cannot work as an +AST rewrite because the expression in old is lifted out of it's context into +one where the only bound variables are the function arguments (see also +history expressions). In most cases this will be a +compiler error complaining that x is unbound, however it is possible that +if there is also a function argument x, then it may silently succeed the +code generation but confusingly fail verification. For instance #[ensures({ let x = 1; old(x) == x })] on a function that has an argument named x would +not hold.

    +

    To handle this correctly we would need an extra check that detects if old +references local variables. That would also enable us to provide a better +error message than the default "cannot find value x in this scope".

    +
  • +
  • +

    Can panicking be expected behavior? Usually preconditions are used to rule +out panics but it is conceivable that a user would want to specify that a +function panics under certain conditions. Specifying this would require an +extension to the current interface.

    +
  • +
  • +

    UB checking. With unsafe rust it is possible to break the type system +guarantees in Rust without causing immediate errors. Contracts must be +cognizant of this and enforce the guarantees as part of the contract or +require users to explicitly defer such checks to use sites. The latter case +requires dedicated support because the potential UB must be reflected in the +havoc.

    +
  • +
  • +

    modifies clauses over patterns. Modifies clauses mention values bound in +the function header and as a user I would expect that if I use a pattern in +the function header then I can use the names bound in that pattern as base +variables in the modifies clause. However modifies clauses are implemented +as assigns clauses in CBMC which does not have a notion of function header +patterns. Thus it is necessary to project any modifies ranges deeper by the +fields used in the matched pattern.

    +
  • +
+ +

Future possibilities

+ +
    +
  • +

    Quantifiers: Quantifiers are like logic-level loops and a powerful +reasoning helper. CBMC has support for both exists and forall, but the +code generation is difficult. The most ergonomic and easy way to implement +quantifiers on the Rust side is as higher-order functions taking Fn(T) -> bool, where T is some arbitrary type that can be quantified over. This +interface is familiar to developers, but the code generation is tricky, as +CBMC level quantifiers only allow certain kinds of expressions. This +necessitates a rewrite of the Fn closure to a compliant expression.

    +
  • +
  • +

    Letting the user supply the harnesses for checking contracts is a source of +unsoundness, if corner cases are not adequately covered. Ideally Kani would +generate the check harness automatically, but this is difficult both because +heap data structures are potentially infinite, and also because it must observe +user-level type invariants.

    +

    A complete solution for this is not known to us but there are ongoing +investigations into harness generation mechanisms in CBMC.

    +

    Function inputs that are non-inductive could be created from the type as the +safe Rust type constraints describe a finite space.

    +

    For dealing with pointers one applicable mechanism could be memory +predicates to declaratively describe the state of the heap both before and +after the function call.

    +

    In CBMC's implementation memory predicates are part of the pre/postconditions. +This does not easily translate to Kani, since we handle pre/postconditions +manually and mainly in proc-macros. There are multiple ways to bridge this +gap, perhaps the easiest being to add memory predicates separately to Kani +instead of as part of pre/postconditions, so they can be handled by forwarding +them to CBMC. However this is also tricky, because memory predicates are used +to describe pointers and pointers only. Meaning that if they are encapsulated +in a structure (such as Vec or RefCell) there is no way of specifying the +target of the predicate without breaking encapsulation (similar to +modifies). In addition there are limitations also on the pointer predicates +in CBMC itself. For instance they cannot be combined with quantifiers.

    +

    A better solution would be for the data structure to declare its own +invariants at definition site which are automatically swapped in on every +contract that uses this type.

    +
  • +
  • +

    What about mutable trait inputs (wrt memory access patters), e.g. a mut impl AccessMe

    +
  • +
  • +

    Trait contracts: Our proposal could be extended easily to handle simple +trait contracts. The macros would generate new trait methods with default +implementations, similar to the functions it generates today. Using sealed +types we can prevent the user from overwriting the generated contract methods. +Contracts for the trait and contracts on it's impls are combined by abstracting +the original method depending on context. The occurrence inside the contract +generated from the trait method is replaced by the impl contract. Any other +occurrence is replaced by the just altered trait method contract.

    +
  • +
  • +

    Cross Session Verification Caching: This proposal focuses on scalability +benefits within a single verification session, but those verification results +could be cached across sessions and speed up verification for large projects +using contacts in the future.

    +
  • +
  • +

    Inductive Reasoning: Describing recursive functions can require that the +contract also recurse, describing a fixpoint logic. This is needed for +instance for linked data structures like linked lists or trees. Consider for +instance a reachability predicate for a linked list:

    +
    struct LL<T> { head: T, next: *const LL<T> }
    +
    +fn reachable(list: &LL<T>, t: &T) -> bool {
    +    list.head == t
    +    || unsafe { next.as_ref() }.map_or(false, |p| reachable(p, t))
    +}
    +
    +
    +
  • +
  • +

    Compositional Contracts: The proposal in this document lacks a +comprehensive handling of type parameters. Contract checking harnesses require +monomorphization. However this means the contract is only checked against a +finite number of instantiations of any type parameter (at most as many as +contract checking harnesses were defined). There is nothing preventing the +user from using different instantiations of the function's type parameters.

    +

    A function (f()) can only interact with its type parameters P through the +traits (T) they are constrained over. We can require T to carry contracts +on each method T::m(). During checking we can use a synthetic type that +abstracts T::m() with its contract. This way we check f() against Ts +contract. Then we later abstract f() we can ensure any instantiations of P +have passed verification of the contract of T::m(). This makes the +substitution safe even if the particular type has not been used in a checking +harness.

    +

    For higher order functions this gets a bit more tricky, as closures are ad-hoc +defined types. Here the contract for the closure could be attached to f() +and then checked for each closure that may be provided. However this does not +work so long as the user has to provide the harnesses, as they cannot recreate +the closure type.

    +
  • +
+
+
1 +

Enforced gates means all uses of constructs (functions, annotations, +macros) in this RFC are an error.

+
+
2 +

The main remaining threat to soundness in the use of +contracts, as defined in this proposal, is the reliance on user-supplied +harnesses for contract checking (explained in item 2 of user +experience). A more thorough discussion on the dangers +and potential remedies can be found in the future +section.

+
+
3 +

For inductively defined types the write set inference +will only add the first "layer" to the write set. If you wish to modify +deeper layers of a recursive type an explicit modifies clause is required.

+
+
4 +

While inferred memory footprints are sound for both safe +and unsafe Rust certain features in unsafe rust (e.g. RefCell) get +inferred incorrectly and will lead to a failing contract check.

+
+
5 +

Slice indices can be place expressions referencing function +arguments, constants and integer arithmetic expressions. Take for example +this Vec method (places simplified vs. actual implementation in std): +fn truncate(&mut self, len: usize). A relatively precise contract for this +method can be achieved with slice indices like so: +#[modifies(self.buf[len..self.len], self.len)]

+
+
6 +

Breaking the pub encapsulation has +unfortunate side effects because it means the contract depends on non-public +elements which are not expected to be stable and can drastically change even +in minor versions. For instance if your project depends on crate a which +in turn depends on crate b, and a::foo has a contract that takes as +input a pointer data structure b::Bar then a::foos assigns contract +must reference internal fields of b::Bar. Say your project depends on the +replacement of a::foo, if b changes the internal representation of +Bar in a minor version update cargo could bump your version of b, +breaking the contract of a::foo (it now crashes because it e.g. references +non-existent fields). +You cannot easily update the contract for a::foo, since it is a +third-party crate; in fact even the author of a could not properly update +to the new contract since their old version specification would still admit +the new, broken version of b. They would have to yank the old version and +explicitly nail down the exact minor version of b which defeats the whole +purpose of semantic versioning.

+
+
7 +

Contracts for functions from +external crates (crates from outside the workspace, which is not quite the +definition of extern crate in Rust) are not checked by default. The +expectation is that the library author providing the contract has performed +this check. See also open question for a discussion on +defaults and checking external contracts.

+
+
8 +

Kani cannot report the occurrence of a contract function to check +in abstracted functions as errors, because the mechanism is needed to verify +mutually recursive functions.

+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0010-quantifiers.html b/rfc/rfcs/0010-quantifiers.html new file mode 100644 index 000000000000..975228e3ee07 --- /dev/null +++ b/rfc/rfcs/0010-quantifiers.html @@ -0,0 +1,362 @@ + + + + + + 0010-quantifiers - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+
    +
  • Feature Name: Quantifiers
  • +
  • Feature Request Issue: #2546 and #836
  • +
  • RFC PR: #
  • +
  • Status: Unstable
  • +
  • Version: 1.0
  • +
+
+

Summary

+

Quantifiers are logical operators that allow users to express that a property or condition applies to some or all objects within a given domain.

+

User Impact

+

There are two primary quantifiers: the existential quantifier (∃) and the universal quantifier (∀).

+
    +
  1. +

    The existential quantifier (∃): represents the statement "there exists." We use to express that there is at least one object in the domain that satisfies a given condition. For example, "∃x P(x)" means "there exists a value x such that P(x) is true."

    +
  2. +
  3. +

    The universal quantifier (∀): represents the statement "for all" or "for every." We use it to express that a given condition is true for every object in the domain. For example, "∀x P(x)" means "for every value x, P(x) is true."

    +
  4. +
+

Rather than exhaustively listing all elements in a domain, quantifiers enable users to make statements about the entire domain at once. This compact representation is crucial when dealing with large or unbounded inputs. Quantifiers also facilitate abstraction and generalization of properties. Instead of specifying properties for specific instances, quantified properties can capture general patterns and behaviors that hold across different objects in a domain. Additionally, by replacing loops in the specification with quantifiers, Kani can encode the properties more efficiently within the specified bounds, making the verification process more manageable and computationally feasible.

+

This new feature doesn't introduce any breaking changes to users. It will only allow them to write properites using the existential (∃) and universal (∀) quantifiers.

+

User Experience

+

We propose a syntax inspired by "Pattern Types". The syntax of existential (i.e., kani::exists) and universal (i.e., kani::forall) quantifiers are:

+
kani::exists(|<var>: <type> [is <range-expr>] | <boolean-expression>)
+kani::forall(|<var>: <type> [is <range-expr>] | <boolean-expression>)
+
+

If <range-expr> is not provided, we assume <var> can range over all possible values of the given <type> (i.e., syntactic sugar for full range |<var>: <type> as .. |). CBMC's SAT backend only supports bounded quantification under constant lower and upper bounds (for more details, see the documentation for quantifiers in CBMC). The SMT backend, on the other hand, supports arbitrary Boolean expressions. In any case, <boolean-expression> should not have side effects, as the purpose of quantifiers is to assert a condition over a domain of objects without altering the state.

+

Consider the following example adapted from the documentation for the from_raw_parts function:

+
use std::ptr;
+use std::mem;
+
+#[kani::proof]
+fn main() {
+    let v = vec![kani::any::<usize>(); 100];
+
+    // Prevent running `v`'s destructor so we are in complete control
+    // of the allocation.
+    let mut v = mem::ManuallyDrop::new(v);
+
+    // Pull out the various important pieces of information about `v`
+    let p = v.as_mut_ptr();
+    let len = v.len();
+    let cap = v.capacity();
+
+    unsafe {
+        // Overwrite memory
+        for i in 0..len {
+            *p.add(i) += 1;
+        }
+
+        // Put everything back together into a Vec
+        let rebuilt = Vec::from_raw_parts(p, len, cap);
+    }
+}
+
+

Given the v vector has non-deterministic values, there are potential arithmetic overflows that might happen in the for loop. So we need to constrain all values of the array. We may also want to check all values of rebuilt after the operation. Without quantifiers, we might be tempted to use loops as follows:

+
use std::ptr;
+use std::mem;
+
+#[kani::proof]
+fn main() {
+    let original_v = vec![kani::any::<usize>(); 100];
+    let v = original_v.clone();
+    for i in 0..v.len() {
+        kani::assume(v[i] < 5);
+    }
+
+    // Prevent running `v`'s destructor so we are in complete control
+    // of the allocation.
+    let mut v = mem::ManuallyDrop::new(v);
+
+    // Pull out the various important pieces of information about `v`
+    let p = v.as_mut_ptr();
+    let len = v.len();
+    let cap = v.capacity();
+
+    unsafe {
+        // Overwrite memory
+        for i in 0..len {
+            *p.add(i) += 1;
+        }
+
+        // Put everything back together into a Vec
+        let rebuilt = Vec::from_raw_parts(p, len, cap);
+        for i in 0..len {
+            assert_eq!(rebuilt[i], original_v[i]+1);
+        }
+    }
+}
+
+

This, however, might unnecessary increase the complexity of the verication process. We can achieve the same effect using quantifiers as shown below.

+
use std::ptr;
+use std::mem;
+
+#[kani::proof]
+fn main() {
+    let original_v = vec![kani::any::<usize>(); 3];
+    let v = original_v.clone();
+    kani::assume(kani::forall(|i: usize is ..v.len() | v[i] < 5));
+
+    // Prevent running `v`'s destructor so we are in complete control
+    // of the allocation.
+    let mut v = mem::ManuallyDrop::new(v);
+
+    // Pull out the various important pieces of information about `v`
+    let p = v.as_mut_ptr();
+    let len = v.len();
+    let cap = v.capacity();
+
+    unsafe {
+        // Overwrite memory
+        for i in 0..len {
+            *p.add(i) += 1;
+        }
+
+        // Put everything back together into a Vec
+        let rebuilt = Vec::from_raw_parts(p, len, cap);
+        assert!(kani::forall(|i: usize is ..len | rebuilt[i] == original_v[i]+1));
+    }
+}
+
+

The same principle applies if we want to use the existential quantifier.

+
use std::ptr;
+use std::mem;
+
+#[kani::proof]
+fn main() {
+    let original_v = vec![kani::any::<usize>(); 3];
+    let v = original_v.clone();
+    kani::assume(kani::forall(|i: usize is ..v.len() | v[i] < 5));
+
+    // Prevent running `v`'s destructor so we are in complete control
+    // of the allocation.
+    let mut v = mem::ManuallyDrop::new(v);
+
+    // Pull out the various important pieces of information about `v`
+    let p = v.as_mut_ptr();
+    let len = v.len();
+    let cap = v.capacity();
+
+    unsafe {
+        // Overwrite memory
+        for i in 0..len {
+            *p.add(i) += 1;
+            if i == 1 {
+              *p.add(i) = 0;
+            }
+        }
+
+        // Put everything back together into a Vec
+        let rebuilt = Vec::from_raw_parts(p, len, cap);
+        assert!(kani::exists(|i: usize is ..len | rebuilt[i] == 0));
+    }
+}
+
+

The usage of quantifiers should be valid in any part of the Rust code analysed by Kani.

+

Detailed Design

+ +

Kani should have the same support that CBMC has for quantifiers. For more details, see Quantifiers.

+

Open questions

+ +
    +
  • Function Contracts RFC - CBMC has support for both exists and forall, but the +code generation is difficult. The most ergonomic and easy way to implement +quantifiers on the Rust side is as higher-order functions taking Fn(T) -> bool, where T is some arbitrary type that can be quantified over. This +interface is familiar to developers, but the code generation is tricky, as +CBMC level quantifiers only allow certain kinds of expressions. This +necessitates a rewrite of the Fn closure to a compliant expression. +
      +
    • Which kind of expressions should be accepted as a "compliant expression"?
    • +
    +
  • +
+

Future possibilities

+ +
    +
  • CBMC has an SMT backend which allows the use of quantifiers with arbitrary Boolean expressions. Kani must include an option for users to experiment with this backend.
  • +
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0011-source-coverage.html b/rfc/rfcs/0011-source-coverage.html new file mode 100644 index 000000000000..95a5f879b6d9 --- /dev/null +++ b/rfc/rfcs/0011-source-coverage.html @@ -0,0 +1,748 @@ + + + + + + 0011-source-coverage - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+ +
+

Summary

+

A source-based code coverage feature for Kani built on top of Rust's coverage instrumentation.

+

User Impact

+

In our first attempt to add a coverage feature fully managed by Kani, we introduced and made available a line coverage option +(see RFC: Line coverage for more details). +This option has since then allowed us to gather more data around the expectations for a coverage feature in Kani.

+

For example, the line coverage output we produced was not easy to interpret +without knowing some implementation details. +Aside from that, the feature requested in +#2795 alludes to the need +of providing coverage-specific tooling in Kani. +Nevertheless, as captured in +#2640, source-based +coverage results provide the clearest and most precise coverage information.

+

In this RFC, we propose an integration with Rust's source-based code coverage +instrumentation. +This integration would allow us to report source-based code coverage results from Kani. +Also, we propose adding a new user-facing, coverage-focused tool called kani-cov. +The tool would allow users to process coverage results generated by Kani and produce +coverage artifacts such as summaries and reports according to their preferences. +In the next section, we will explain in more detail how we +expect kani-cov to assist with coverage-related tasks.

+

With these changes, we expect our coverage options to become more flexible, +precise and efficient. These options are expected to replace the previous +options available through the line coverage feature. +In the last section of this RFC, we will also discuss +the requirements for a potential integration of this coverage feature with the +LLVM toolchain.

+

User Experience

+

The proposed experience is partially inspired by that of the most popular +coverage frameworks. +First, let us delve into the LLVM coverage workflow, followed by an explanation +of our proposal.

+

The LLVM code coverage workflow

+

The LLVM project is home to one of the most popular code coverage frameworks. +The workflow associated to the LLVM framework is described in the documentation for +source-based code coverage1, +but we briefly describe it here to better relate it with our proposal.

+

In short, the LLVM code coverage workflow follows three steps:

+
    +
  1. Compiling with coverage enabled. This causes the compiler to generate an instrumented program.
  2. +
  3. Running the instrumented program. This generates binary-encoded .profraw files.
  4. +
  5. Using tools to aggregate and export coverage information into other formats.
  6. +
+

When working in a cargo project, step 1 can be done through this command:

+
RUSTFLAGS='-Cinstrument-coverage' cargo build
+
+

The same flag must to be used for step 2:

+
RUSTFLAGS='-Cinstrument-coverage' cargo run
+
+

This should populate the directory with at least one .profraw file. +Each .profraw file corresponds to a specific source code file in your project.

+

At this point, we will have produced the artifacts that we generally require for +the LLVM tools:

+
    +
  1. The instrumented binary which, in addition to the instrumented program, +contains additional information (e.g., the coverage mappings) required to +interpret the profiling results.
  2. +
  3. The .profraw files which essentially includes the profiling results +(e.g., counter values) for each function of the corresponding source code file.
  4. +
+

For step 3, the commands will depend on what kind of results we want. +Most likely we will have to merge the .profraw files and produce a .profdata +file as follows:

+
llvm-profdata merge -sparse *.profraw -o output.profdata
+
+

The resulting .profdata file will contain the aggregated coverage results from +the .profraw files passed to the merge command.

+

Then, we can use a command such as

+
llvm-cov show target/debug/binary —instr-profile=output.profdata -show-line-counts-or-regions
+
+

to visualize the code coverage through the terminal as in the image:

+

Source-based code coverage with llvm-cov

+

or the command

+
llvm-cov report target/debug/binary --instr-profile=output.profdata --show-region-summary
+
+

to produce coverage summaries like this:

+
Filename                                             Regions    Missed Regions     Cover   Functions  Missed Functions  Executed       Lines      Missed Lines     Cover    Branches   Missed Branches     Cover
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+/long/long/path/to/my/project/binary/src/main.rs           9                 3    66.67%           3                 1    66.67%          14                 4    71.43%           0                 0         -
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+TOTAL                                                      9                 3    66.67%           3                 1    66.67%          14                 4    71.43%           0                 0         -
+
+
1 +

The LLVM project refers to their own coverage feature as +source-based code coverage. It is not rare to see the term region +coverage being used instead to refer to the same thing. That is because +LLVM's source-based code coverage feature can report coverage for code +regions, but other coverage frameworks do not support the concept of code +regions.

+
+

The Kani coverage workflow

+

The two main components of the Kani coverage workflow that we propose are the +following:

+
    +
  1. The existing --coverage flag that drives the coverage workflow in Kani, +emits raw coverage data (as in the .profraw files), and produces basic +coverage results by default.
  2. +
  3. A new subcommand cov that allows users to further process raw coverage +information emitted by Kani to produce customized coverage results (i.e., +different to the ones produced by default with the --coverage option). +The cov subcommand is an alias for the kani-cov tool.
  4. +
+

In contrast to the LLVM workflow, where human-readable coverage results can +be produced only after a sequence of LLVM tool commands, we provide some +coverage results by default. +This aligns better with our UX philosophy, and removes the need for a wrapper +around our coverage features like +cargo-llvm-cov. +Alternatively, the cov subcommand offers the ability of producing more +specific coverage results if needed. +We anticipate the cov subcommand being particularly useful in less standard +project setups, giving the users the flexibility required to produce coverage +results tailored to their specific needs.

+

In the following, we describe each one of these components in more detail.

+

The --coverage option

+

The default coverage workflow will be kicked off through the unstable +--coverage option:

+
cargo kani --coverage -Zsource-coverage
+
+

The main difference with respect to the regular verification workflow is that, +at the end of the verification-based coverage run, Kani will generate two coverage +results:

+
    +
  • A coverage summary corresponding to the coverage achieved by the harnesses +included in the verification run. This summary will be printed after the +verification output.
  • +
  • A coverage report corresponding to the coverage achieved by the harnesses +included in the verification run. The report will be placed in the same target +directory where the raw coverage files are put. The path to the report will +also be printed after the verification output.
  • +
+

Therefore, a typical --coverage run could look like this:

+
VERIFICATION:- SUCCESSFUL
+
+Coverage Results:
+
+| Filename | Regions | Missed Regions | Cover | Functions | Missed Functions | Cover |
+| -------- | ------- | -------------- | ----- | --------- | ---------------- | ----- |
+| main.rs  |       9 |              3 | 66.67 |         3 |                1 | 33.33 |
+| file.rs  |      11 |              5 | 45.45 |         2 |                1 | 50.00 |
+
+Coverage report available in target/kani/x86_64-unknown-linux-gnu/cov/kani_2024-04-26_15-30-00/report/index.html
+
+

The cov subcommand

+

The cov subcommand will be used to process raw coverage information generated +by Kani and produce coverage outputs as indicated by the user. +Hence, the cov subcommand corresponds to the set of LLVM tools +(llvm-profdata, llvm-cov, etc.) that are used to produce coverage outputs +through the LLVM coverage workflow.

+

In contrast to LLVM, we will have a single subcommand for all Kani +coverage-related needs. The cov subcommand will just call the kani-cov tool, +which is expected to be shipped along the rest of Kani binaries.

+

We suggest that the subcommand initially offers two options:

+
    +
  1. An option to merge the coverage results from one or more files and coverage +mappings2 into a single file.
  2. +
  3. An option to produce coverage outputs from coverage results, including summaries +or coverage reports in human-readable formats (e.g., HTML).
  4. +
+

Let's assume that we have run cargo kani --coverage -Zsource-coverage and +generated coverage files in the my-coverage folder. Then, we would use cargo kani cov as follows to combine the coverage results3 for all +harnesses:

+
cargo kani cov --merge my-coverage/*.kaniraw -o my-coverage.kanicov
+
+

Let's say the user is first interested in reading a coverage summary through the +terminal. +They can use the --summary option for that:

+
cargo kani cov --summary my-coverage/default.kanimap -instr-profile=my-coverage.kanicov
+
+

The command could print a coverage summary like:

+
| Filename | Regions | Missed Regions | Cover | Functions | ...
+| -------- | ------- | -------------- | ----- | --------- | ...
+| main.rs  |       9 |              3 | 66.67 |         3 | ...
+[...]
+
+

Now, let's say the user wants to produce an HTML report of the coverage results. +They will have to use the --report option for that:

+
cargo kani cov --report my-coverage/default.kanimap -format=html -instr-profile=my-coverage.kanicov -o coverage-report
+
+

This time, the command will generate a coverage-report folder including a +browsable HTML webpage that highlights the regions covered in the source +according to the coverage results in my-coverage.kanicov.

+
4 +

The llvm-cov tool includes the option +gcov to +export into GCC's coverage format Gcov, +and the option +export +to export into the LCOV format. These may be good options to consider for +kani-cov in the future but we should focus on basic formats for now.

+
+
3 +

Options to exclude certain coverage results (e.g, from the +standard library) will likely be part of this option.

+
+
2 +

Coverage mappings essentially provide a snapshot of the source +code reports for items that otherwise are unreachable or have been sliced +away during the compilation process.

+
+

Integration with the Kani VS Code Extension

+

We will update the coverage feature of the +Kani VS Code Extension +to follow this new coverage workflow. +In other words, the extension will first run Kani with the --coverage option and +use kani cov to produce a .kanicov file with the coverage results. +The extension will consume the source-based code coverage results and +highlight region coverage in the source code seen from VS Code.

+

We could also consider other coverage-related features in order to enhance the +experience through the Kani VS Code Extension. For example, we could +automatically show the percentage of covered regions in the status bar by +additionally extracting a summary of the coverage results.

+

Finally, we could also consider an integration with other code coverage tools. +For example, if we wanted to integrate with the VS Code extensions +Code Coverage or +Coverage Gutters, +we would only need to extend kani-cov to export coverage results to the LCOV +format or integrate Kani with LLVM tools as discussed in Integration with LLVM.

+

Detailed Design

+

In this section, we provide more details on:

+
    +
  • The Rust coverage instrumentation and how it can be integrated into +Kani to produce source-based code coverage results.
  • +
  • The proposed coverage workflow to be run by default in Kani when the +--coverage option is used.
  • +
+

This information is mostly intended as a reference for Kani contributors. +Currently, the Rust coverage instrumentation continues to be developed. Because +of that, Rust toolchain upgrades may result in breaking changes to our own +coverage feature. This section should help developers to understand the general +approach and resolve such issues by themselves.

+

The Rust coverage instrumentation

+

The Rust compiler includes two code coverage implementations:

+
    +
  • A source-based coverage implementation which uses LLVM's coverage +instrumentation to generate precise coverage data. This implementation can be +enabled with -C instrument-coverage.
  • +
  • A Gcov-based coverage implementation that derives coverage data based on +DebugInfo. This implementation can be enabled with -Z profile.
  • +
+

The Instrumentation-based Code Coverage +chapter from the rustc book describes in detail how to enable and use the LLVM +instrumentation-based coverage feature. In contrast, the +LLVM Source-Based Code Coverage +chapter from the rustc development guide documents how the LLVM +coverage instrumentation is performed in the Rust compiler.

+

In this section, we will first summarize some information from the +LLVM Source-Based Code Coverage +chapter, limited to details which are relevant to the development of the +source-based coverage feature in Kani. Then, we will explain how Kani taps into +the Rust coverage instrumentation to perform its own coverage instrumentation +and be able to report source-based code coverage results. This will also include +mentions to current issues with this implementation, which we plan to further +discuss in Future possibilities.

+

Understanding the Rust coverage instrumentation

+

The LLVM coverage instrumentation is implemented in the Rust compiler as a +MIR pass called InstrumentCoverage.

+

The MIR pass first builds a coverage-specific version of the MIR Control Flow +Graph (CFG) from the MIR. The initial version of this CFG is based on the MIR's +BasicBlocks, which then gets refined by combining blocks that can be chained +from a coverage-relevant point of view. The final version of the coverage CFG is +then used to determine where to inject the +StatementKind::Coverage +statements in order to measure coverage for a single region coverage span.

+

The injection of StatementKind::Coverage statements is the main result we are +interested in for the integration with Kani. Additionally, the instrumentation +will also attach the +FunctionCoverageInfo +structure to each function's body.5 +This result is also needed at the moment because coverage statements do not +include information on the code region they are supposed to cover. +However, FunctionCoverageInfo contains the +coverage mappings, +which represent the relation between +coverage counters +and code regions.

+

As explained in MIR Pass: +InstrumentCoverage, +many coverage statements will not be converted into a physical +counter6. Instead, they will be converted into a +coverage-counter expression that can be calculated based on other coverage +counters. We highly recommend looking at the example in MIR Pass: +InstrumentCoverage +to better understand how this works. This optimization is mainly done for +performance reasons because incrementing a physical counter causes a +non-negligible overhead, especially within loops.

+

The (StatementKind::)Coverage statements that are injected by the Rust coverage +instrumentation contain a CoverageKind field indicating the type of coverage counter. The variant +CounterIncrement +represents physical counters, while +ExpressionUsed +represents the counter expressions that we just discussed. +Other variants such as SpanMarker or BlockMarker are not relevant to this +work since they should have been erased after the InstrumentCoverage pass.

+
5 +

It is important to note that the StableMIR interface does +not include FunctionCoverageInfo in function bodies. Because of that, we +need to pull it from the internal rustc function bodies.

+
+
6 +

By physical counter, we refer to a global program +variable that is initialized to zero and incremented by one each time that +the execution passes through.

+
+

Integrating the instrumentation into Kani

+

Now that we have explained what the Rust coverage instrumentation does at a high +level, we should be ready to discuss how it can be used from Kani. Here, we will +follow an approach where, during the codegen stage, we generate a Kani +reachability check for each code region and, after the verification stage, we +postprocess the information in those checks to generate the coverage +information. So this section will essentially be a retelling of the +implementation in #3119, and +we will discuss variations/extensions of this approach in the +appropriate sections.

+

Clearly, the first step is adding -C instrument-coverage to the rustc flags +we use when calling the compiler to codegen. This flag enables the Rust coverage +instrumentation that we discussed earlier, resulting in

+
    +
  1. the injection of +Coverage +statements in the MIR code, and
  2. +
  3. the inclusion of FunctionCoverageInfo in function bodies.
  4. +
+

The next step is handling the Coverage statements from codegen_statement.

+

Each Coverage statement contains opaque coverage +information7 of the +CoverageKind +type which can be processed to determine the type of coverage counter +(CounterIncrement for physical counters, ExpressionUsed for counter +expressions) and the ID number of the counter. These two pieces of information allow us to +uniquely identify the counter within a given function. For example, +CounterIncrement(0) would generally refer to the first physical counter in the +function.

+

Unfortunately, the CoverageKind information does not tell us anything about +the code region that the counter covers. However, data about the code region can +be pulled from the coverage mappings included in the FunctionCoverageInfo that +is attached to the (internal) function body. Note that the coverage mappings +includes information about all the coverage counters in a function, even for +counters which have been dropped. Matching the CoverageKind information with +that of the counters in the coverage mappings allows us to retrieve the code +region for any counter.

+

Using all this data, for each coverage statement8 we generate a coverage check +that maintains the essence of the coverage checks described in the RFC for line +coverage:

+
+

Coverage checks are a new class of checks similar to cover checks. +The main difference is that users cannot directly interact with coverage checks (i.e., they cannot add or remove them manually). +Coverage checks are encoded as an assert(false) statement (to test reachability) with a fixed description. +In addition, coverage checks are:

+
    +
  • Hidden from verification results.
  • +
  • Postprocessed to produce coverage results.
  • +
+
+

Therefore, the last step is to postprocess the results from coverage checks to +produce coverage results. This is not too complicated to do since the checks +already include the counter information (type + ID) and the function name in +the check's description. If the span of the code region is also included +(this is what #3119 is +currently doing), we can directly generate a primitive output like this:

+
<file_path> (<function_name>)
+ * <region_start> - <region_end> <status>
+ * ...
+ * <region_start> - <region_end> <status>
+
+

For example, for the test case in +#3119 we report this:

+
src/main.rs (main)
+ * 14:1 - 19:2 COVERED
+
+src/main.rs (test_cov)
+ * 5:1 - 6:15 COVERED
+ * 6:19 - 6:28 UNCOVERED
+ * 7:9 - 7:13 COVERED
+ * 9:9 - 9:14 UNCOVERED
+ * 11:1 - 11:2 COVERED
+
+
+

NOTE: This section has been written according to the implementation in +#3119, which currently +produces a text-based output like the one shown above. There is ongoing work to +store the coverage mappings in a separate file (as described in the next +section), which would save us the need to attach code region data to the +coverage checks.

+
+
7 +

The Rust compiler uses the Opaque type to prevent +others from interfacing with unstable types (e.g., the Coverage type +here). +Nonetheless, this can be worked around by serializing its contents and parsing +it back into an internal data type.

+
+
8 +

We could follow an alternative approach where we +do not instrument each coverage statement, but only those that correspond to +physical counters. Unfortunately, doing so would lead to incorrect coverage +results due to the arithmetic nature of expression-based counters. We elaborate +on this topic in the later parts of this document.

+
+

The default coverage workflow in Kani

+

In this section, we describe the default --coverage workflow from a +developer's point of view. This will hopefully help developers understand how +the different coverage components in Kani are connected. For example, we'll +describe the raw coverage information that gets produced throughout the default +--coverage workflow and define the basic cov commands that it will execute.

+

The main difference with respect to the regular verification workflow is that, +at the end of the verification-based coverage run, Kani will generate two types +of files:

+
    +
  • One single file .kanimap file for the project. This file will contain the +coverage mappings for the project's source code.
  • +
  • One .kaniraw file for each harness. This file will contain the +verification-based results for the coverage-oriented properties corresponding +to a given harness.
  • +
+

Note that .kaniraw files correspond to .profraw files in the LLVM coverage +workflow. Similarly, the .kanimap file corresponds to the coverage-related +information that's embedded into the project's binaries in the LLVM coverage +workflow.9

+

The files will be written into a new timestamped directory associated with the +coverage run. The path to this directory will be printed to standard output in +by default. For example, the draft implementation +writes the coverage files into the target/kani/<target_triple>/cov/ directory.

+

Users aren't expected to read the information in any of these files. +Therefore, there's no need to restrict their format. +The draft implementation +uses the JSON format but we might consider using a binary format if it doesn't +scale.

+

In addition, Kani will produce two types of coverage results:

+
    +
  1. A coverage summary with the default options.
  2. +
  3. A terminal-based coverage report with the default options. However, we will +only do this if the program is composed of a single source +file10.
  4. +
+
9 +

Note that the .kanimap generation isn't implemented in +#3119. The draft +implementation of +kani-cov simply reads +the source files referred to by the code coverage checks, but it doesn't get +information about code trimmed out by the MIR linker.

+
+
10 +

In other words, standalone kani would always emit +these terminal-based reports, but cargo kani would not unless the project +contains a single Rust file (for example, src/main.rs).

+
+

Rationale and alternatives

+

Other coverage implementations

+

In a previous version of this feature, we used an ad-hoc coverage implementation. +In addition to being very inefficient11, the line-based coverage +results were not trivial to interpret by users. +At the moment, there's only another unstable, GCC-compatible code coverage implementation +based on the Gcov format. The Gcov format is line-based so it's not able +to report region coverage results. +In other words, it's not as advanced nor precise as the source-based implementation.

+
11 +

Actual performance benchmarks to follow in +#3119.

+
+

Open questions

+
    +
  • Do we want to instrument dependencies by default? Preliminary benchmarking results show a slowdown of 100% and greater. +More evaluations are required to determine how we handle instrumentation for dependencies, and what options we might want +to provide to users.
  • +
  • How do we handle features/options for kani-cov? In particular, do we need more details in this RFC?
  • +
+

Future possibilities

+

Integration with LLVM

+

As part of this work, we explored a potential integration with the LLVM +framework. The idea behind such an integration would essentially involve +producing coverage results in formats compatible with the LLVM framework (e.g., +the .profraw format). The main advantage of integrating with the LLVM +framework in this way is that we would not need a tool like kani-cov to +aggregate coverage results; we could just use LLVM tools such as llvm-profdata +and llvm-cov to consume them.

+

However, at this time we recommend against integrating with LLVM due to these reasons:

+
    +
  1. Generating the instrumented binary used in the LLVM coverage +workflow requires a standard rustc +compilation with --cfg kani in addition to other flags including -C instrument-coverage. This is likely to result in compilation errors since the +standard rustc backend cannot produce code for Kani APIs, for example.
  2. +
  3. Producing the .profraw files requires executing the instrumented binary at +least once. This would be an issue for Rust projects which assume a particular +environment for their execution.
  4. +
  5. There are no stable interfaces to create or modify files in formats +compatible with the LLVM framework. Even though the documentation for the LLVM +Code Coverage Mapping Format +is excellent, the easiest way to interact with files on these format is through +LLVM tools (e.g., +llvm-cov) +which bring in many other LLVM dependencies. During our exploration, we +attempted to decode and re-encode files in the .profraw to set the counter +data to the values obtained during verification. To this end, we tried tools +like llvm-profparser which +can be used as a replacement for llvm-profdata and llvm-cov but failed to +parse coverage files emitted by the Rust compiler (this is also related to the +next point). Another crate that we used is +coverage-dump, +a recent tool in the Rust compiler used for testing purposes. coverage-dump +extracts coverage mappings from LLVM IR assembly files (i.e., human-readable +*.ll files) but does not work with the binary-encoded formats. Finally, we +also built some ad-hoc tooling to perform these modifications but it soon +became evident that we would need to develop it further in order to handle any +program.
  6. +
  7. LLVM releases a new version approximately every six months. This would +likely result in another "toolchain update" problem for Kani in order to +provide compatibility with newer LLVM versions. Moreover, the Rust compiler +supplies their own version of LLVM tools (rust-profdata, rust-cov, etc.) +which are fully compatible with coverage-related artifacts produced by rustc.
  8. +
+

Optimization with coverage-counter expressions

+

In the subsection related to the +integration, we noted that we could +follow an alternative approach where we only instrument coverage statements that +correspond to physical counters. In fact, this would be the logical choice since +the replacement of physical counters by expression-based counters would also be +a performance optimization for us.

+

However, the expressions used in expression-based counters are built with the +arithmetic operators Add (+) and Sub (-). On the other hand, the +coverage checks performed by Kani have a boolean meaning: you either cover a +region or you do not. Thus, there are many cases where these two notions of +coverage counters are incompatible. For example, let's say we have this +function:

+
fn check_value(val: u32) {
+   if val == VALUE {
+      do_this();
+   } else {
+      do_that();
+   }
+   do_something_else();
+}
+
+

One way to optimize the counters in this function is to have two physical +counters for the branches of the if statement (c1 and c2), and then an +expression-based counter associated to the do_something_else() statement +adding those (i.e., c3 = c1 + c2). If we have, for example, two executions for +this program, with each one taking a different branch, then the results for the +coverage counters will be c1 = 1, c2 = 1 and c3 = c1 + c2 = 2.

+

But what does c3 = 2 mean in the context of a verification-based coverage +result? That is not clear. For instance, in a Kani trace, you could have a +nondeterministic value for val which just happens to be val == VALUE and not +at the same time. This would result in the same counters (c1 = 1, c2 = 1 and +c3 = 2), but the program is being run only once!

+

Note that finding a verification-based replacement for the runtime operators in +counter-based expressions is an interesting research topic. If we could +establish a relation between the runtime and verification expressions, then we +could avoid the instrumentation of coverage checks for expression-based +counters. For example, could we replace the Add operator (+) with an Or +operator (||)? Intuitively, this makes sense since verification-based coverage +counters are binary. It also seems to work for our example since covering any of +the branches should result in the do_something_else() statement being covered +as well, with the counter values now being c1 = 1, c2 = 1 and c3 = 1. +However, it is not clear that this would work for all cases, nor it is clear +that we can replace Sub with another verification-based operator.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0012-loop-contracts.html b/rfc/rfcs/0012-loop-contracts.html new file mode 100644 index 000000000000..e342c0f2a4d4 --- /dev/null +++ b/rfc/rfcs/0012-loop-contracts.html @@ -0,0 +1,388 @@ + + + + + + 0012-loop-contracts - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+
    +
  • Feature Name: Loop Contracts
  • +
  • Feature Request Issue: #3168
  • +
  • RFC PR: #3167
  • +
  • Status: Under Review
  • +
  • Version: 1
  • +
  • Proof-of-concept:
  • +
+
+

Summary

+

Loop contracts provide way to safely abstract loops of a program, typically +in order to accelerate the verification process, and remove the loop unwinding +bounds. The key idea is to over-approximate the possible set of program states, +while still being precise enough to be able to prove the desired property.

+

User Impact

+

Loop contracts provide an interface for a verified, sound abstraction. +The goal for specifying loop contracts in the source code is two fold:

+
    +
  • Unbounded verification: Currently, proving correctness +(i.e. assertions never fail) on programs with unbounded control flow (e.g. +loops with dynamic bounds) Kani requires unwinding loops for a large number of +times, which is not always feasible. Loop contracts provide a way to abstract +out loops, and hence remove the need for unwinding loops.
  • +
  • Faster CI runs: In most cases, the provided contracts would also significantly +improve Kani's verification time since all loops would be unrolled only to +a single iteration.
  • +
+

Loop contracts are completely optional with no user impact if unused. This +RFC proposes the addition of new attributes, and functions, that shouldn't +interfere with existing functionalities.

+

User Experience

+

A loop contract specifies the behavior of a loop as a boolean predicate +(loop invariants clauses) with certain frames conditions (loop modifies clauses) +that can be checked against the loop implementation, and used to abstract out +the loop in the verification process.

+

We illustrate the usage of loop contracts with an example. +Consider the following program:

+
fn simple_loop() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    while x > 1{
+        x = x - 1;
+    };
+
+    assert!(x == 1);
+}
+
+

The loop in the simple_loop function keep subtracting 1 from x until x is 1. +However, Kani currently needs to unroll the loop for u64::MAX number of times +to verify the assertion at the end of the program.

+

With loop contracts, the user can specify the behavior of the loop as follows:

+
fn simple_loop_with_loop_contracts() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    #[kani::loop_invariant(x >= 1)]
+    while x > 1{
+        x = x - 1;
+    };
+
+    assert!(x == 1);
+}
+
+

The loop invariant clause #[kani::loop_invariant(x >= 1)] specifies the loop +invariants that must hold at the beginning of each iteration of the loop right before +checking the loop guard.

+

In this case, Kani verifies that the loop invariant x >= 1 is inductive, i.e., +x is always greater than or equal to 1 at each iteration before checking x > 1.

+

Also, once Kani proved that the loop invariant is inductive, it can safely use the loop +invariants to abstract the loop out of the verification process. +The idea is, instead of exploring all possible branches of the loop, Kani only needs to +prove those branches reached from an arbitrary program state that satisfies the loop contracts, +after the execution of one iteration of the loop.

+

So, for loops without break statements, we can assume all post-states of the loop satisfying +inv && !loop_guard for proving post-loops properties. +The requirement of satisfying the negation of the loop guard comes from the fact that a path +exits loops without break statements must fail the loop guard.

+

For example, applying loop contracts in simple_loop function is equivalent to the following:

+
fn simple_loop_transformed() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    x = kani::any(); // Arbitrary program state that
+    kani::assume( !(x > 1) && x >= 1); // satisfies !`guard` && `inv` 
+
+    assert!(x == 1);
+}
+
+

The assumption above is actually equivalent to x == 1, hence the assertion at the end +of the program is proved.

+

Write Sets and Havocking

+

For those memory locations that are not modified in the loop, loop invariants state +that they stay unchanged throughout the loop are inductive. In other words, Kani should +only havoc the memory locations that are modified in the loop. This is achieved by +specifying the modifies clause for the loop. For example, the following program:

+
fn simple_loop_two_vars() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+    let mut y: u64 = 1;
+
+    #[kani::loop_invariant(x >= 1)]
+    #[kani::loop_modifies(x)]
+    while x > 1{
+        x = x - 1;
+    };
+
+    assert!(x == 1);
+    assert!(y == 1);
+}
+
+

write to only x in the loop, hence the modifies clause contains only x. +Then when use the loop contracts to abstract the loop, Kani will only havoc the memory +location x and keep y unchanged. Note that if the modifies clause contains also +y, Kani will havoc both x and y, and hence violate the assertion y == 1.

+

Kani can employs CBMC's write set inference to infer the write set of the loop. +So users have to specify the modifies clauses by their self only when the inferred write +sets are not complete---there exists some target that could be written to in the loop but +is not in the inferred write set.

+

Proof of termination

+

Loop contracts also provide a way to prove the termination of the loop. +Without the proof of termination, Kani could report success of some assertions that +are actually unreachable due to non-terminating loops. +For example, consider the following program:

+
fn simple_loop_non_terminating() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    #[kani::loop_invariant(x >= 1)]
+    while true{
+        x = x;
+    };
+
+    assert!(x >= 1);
+}
+
+

After abstracting the loop, the loop will be transformed to no-op, and the assertion +x >= 1 will be proved. However, the loop is actually an infinite loop, and the +assertion will never be reached.

+

For this reason, Kani will also require the user to provide a decreases clause that +specifies a decreasing expression to prove the termination of the loop. For example, in

+
fn simple_loop_terminating() {
+    let mut x: u64 = kani::any_where(|i| *i >= 1);
+
+    #[kani::loop_invariant(x >= 1)]
+    #[kani::loop_decreases(x)]
+    while x > 1{
+        x = x - 1;
+    };
+
+    assert!(x >= 1);
+}
+
+

, the decreases clause #[kani::loop_decreases(x)] specifies that the value of x +decreases at each iteration of the loop, and hence the loop will terminate.

+

Detailed Design

+

Kani implements the functionality of loop contracts in three places.

+
    +
  1. Procedural macros loop_invariant, loop_modifies, and loop_decreases.
  2. +
  3. Code generation for builtin functions expanded from the above macros.
  4. +
  5. GOTO-level loop contracts using CBMC's contract language generated in +kani-compiler.
  6. +
+

Procedural macros loop_invariant, loop_modifies, and loop_decreases.

+

We will implement the three proc-macros loop_invariant, loop_modifies, and loop_decreases to +embed the annotation logic as Rust code. Kani will then compile them into MIR-level code.

+

Code Generation for Builtin Functions

+

Then in the MIR, we codegen the loop contracts as GOTO-level expressions and annotate them +into the corresponding loop latches---the jumps back to the loop head.

+

The artifact goto-instrument in CBMC will extract the loop contracts from the named-subs +of the loop latch, and then apply and prove the extracted loop contracts.

+

GOTO-Level Havocing

+

The ordinary havocing in CBMC is not aware of the type constraints of Rust type. +Hence, we will use customized havocing functions for modifies targets. In detail, +Kani will generate code for the definition of corresponding kani::any() functions +for each modifies target. Then Kani will create a map from the modifies target to the +the name of its kani::any() function, and add the map to the loop latch too.

+

On the CBMC site, goto-instrument will extract the map and instrument the customized +havocing functions for the modifies targets.

+

Rationale and alternatives

+

Rust-Level Transformation vs CBMC

+

Besides transforming the loops in GOTO level using goto-instrument, +we could also do the transformation in Rust level using procedural macros, or +in MIR level.

+

There are two reasons we prefer the GOTO-level transformation. +First, goto-instrument is a mature tool that can correctly instrument the frame +condition checking for the transformed loop, which will save us from reinventing +the error-prone wheel. Second, the loop contracts synthesis tool we developed and +are developing are all based on GOTO level. Hence, doing the transformation in +the GOTO level will make the integration of loop contracts with the synthesis tool +easier.

+

Open questions

+
    +
  • How do we integrate loop contracts with the synthesis tool? When the user-provided +loop contracts are not enough prove the harness, we expect the loop-contract synthesizer +can fix the loop contracts.
  • +
  • How do we translate back modify targets that inferred by CBMC to Rust level?
  • +
  • It is not clear how the CBMC loop modifies inference works for Rust code. We need to +experiment more to decide what would be the best UX of using loop modifies.
  • +
  • How do we handle havocing in unsafe code where it is fine to break the safety invariant +of Rust? In that case, we may need havocing function that preserves validity invariant +but not safety invariant.
  • +
  • What is the proper mechanism for users to specify the loops that they want to opt-out from applying loop contracts, and (optionally) the unwind numbers for them. Such options should be per-harness.
  • +
+

Future possibilities

+
    +
  • We can employ CBMC's decreases inference to infer the decreases clauses to reduce the +user burden of specifying the decreases clauses.
  • +
+ +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/rfcs/0013-list.html b/rfc/rfcs/0013-list.html new file mode 100644 index 000000000000..fdcd368231bd --- /dev/null +++ b/rfc/rfcs/0013-list.html @@ -0,0 +1,336 @@ + + + + + + 0013-list - Kani RFC Book + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+
    +
  • Feature Name: List Subcommand
  • +
  • Feature Request Issue: #2573, #1612
  • +
  • RFC PR: #3463
  • +
  • Status: Unstable
  • +
  • Version: 2
  • +
+
+

Summary

+

Add a subcommand list that, for each crate under verification, lists the information relevant to its verification.

+

User Impact

+

Currently, there is no automated way for a user to gather metadata about Kani's integration with their project. If, for example, a user wants a list of harnesses for their project, they must search for all the relevant contract attributes (currently #[proof] or #[proof_for_contract]) themselves. If done manually, this process is tedious, especially for large projects. Even with a shell script, it is error-prone--if, for example, we introduce a new type of proof harness, users would have to account for it when searching their project.

+

Internally, this feature will be useful for tracking our customers' use of Kani and our progress with standard library verification. Externally, users can leverage this feature to get a high-level view of which areas of their projects have harnesses (and, by extension, which areas are still in need of verification).

+

This feature will not cause any regressions for exisiting users.

+

User Experience

+

Users run a list subcommand, which prints metadata about the harnesses and contracts in each crate under verification. The subcommand takes two options:

+
    +
  • --message-format=[pretty|json]: choose the output format. The default is pretty, which prints to the terminal. The json option creates and writes to a JSON file instead.
  • +
  • --std: this option should be specified when listing the harnesses and contracts in the standard library. This option is only available for kani list (not cargo kani list), which mirrors the verification workflow for the standard library.
  • +
+

This subcommand does not fail. In the case that it does not find any harnesses or contracts, it prints a message informing the user of that fact.

+

Pretty Format

+

The default format, pretty, prints a "Contracts" table and a "Standard Harnesses" list. +Each row of the "Contracts" table consists of a function under contract and its contract harnesses. +The results are printed in lexicographic order.

+

For example:

+
Kani Rust Verifier 0.54.0 (standalone)
+
+Contracts:
+|-------|-------------------------|--------------------------------------------------|
+|       | Function                | Contract Harnesses (#[kani::proof_for_contract]) |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::impl::bar      | example::verify::check_bar                       |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::impl::baz      | example::verify::check_baz                       |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::impl::foo      | example::verify::check_foo_u32                   |
+|       |                         | example::verify::check_foo_u64                   |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::impl::func     | example::verify::check_func                      |
+|-------|-------------------------|--------------------------------------------------|
+|       | example::prep::parse    | NONE                                             |
+|-------|-------------------------|--------------------------------------------------|
+| Total | 5                       | 5                                                |
+|-------|-------------------------|--------------------------------------------------|
+
+Standard Harnesses (#[kani::proof]):
+1. example::verify::check_modify
+2. example::verify::check_new
+
+

All sections will be present in the output, regardless of the result. +If there are no harnesses for a function under contract, Kani inserts NONE in the "Contract Harnesses" column. +If the "Contracts" section is empty, Kani prints a message that "No contracts or contract harnesses were found." +If the "Standard Harnesses" section is empty, Kani prints a message that "No standard harnesses were found."

+

JSON Format

+

If the user wants an output format that's more easily parsed by a script, they can use the json option.

+

The JSON format will contain the same information as the pretty format, with the addition of file paths and file version. +The file version will use semantic versioning. +This way, any users relying on this format for their scripts can detect when we've released a new major version and update their logic accordingly.

+

For example:

+
{
+    kani-version: 0.54,
+    file-version: 0.1,
+    standard-harnesses: [
+        {
+            file: /Users/johnsmith/example/kani_standard_proofs.rs
+            harnesses: [
+                example::verify::check_modify,
+                example::verify::check_new
+            ]
+        },
+    ],
+    contract-harnesses: [
+        {
+            file: /Users/johnsmith/example/kani_contract_proofs.rs
+            harnesses: [
+                example::verify::check_bar,
+                example::verify::check_baz,
+                example::verify::check_foo_u32,
+                example::verify::check_foo_u64, 
+                example::verify::check_func 
+            ]
+        },
+    ],
+    contracts: [
+        {
+            function: example::impl::bar
+            file: /Users/johnsmith/example/impl.rs
+            harnesses: [example::verify::check_bar]
+        },
+        {
+            function: example::impl::baz
+            file: /Users/johnsmith/example/impl.rs
+            harnesses: [example::verify::check_baz]
+        },
+        {
+            function: example::impl::foo
+            file: /Users/johnsmith/example/impl.rs
+            harnesses: [
+                example::verify::check_foo_u32,
+                example::verify::check_foo_u64
+            ]
+        },
+        {
+            function: example::impl::func
+            file: /Users/johnsmith/example/impl.rs
+            harnesses: [example::verify::check_func]
+        },
+        {
+            function: example::prep::parse
+            file: /Users/johnsmith/example/prep.rs
+            harnesses: []
+        }
+    ],
+    totals: {
+        standard-harnesses: 2,
+        contract-harnesses: 5,
+        functions-with-contracts: 5,
+    }
+}
+
+

All sections will be present in the output, regardless of the result. +If there is no result for a given field (e.g., there are no contracts), Kani will output an empty list (or zero for totals).

+

Software Design

+

Driver/Metdata Changes

+

We add a new list subcommand to kani-driver, which invokes the compiler to collect metadata, then post-processes that metadata and outputs the result. +We extend KaniMetadata to include a new field containing each function under contract and its contract harnesses.

+

Compiler Changes

+

In codegen_crate, we update the generation of KaniMetadata to include the new contracts information. +We iterate through each local item in the crate. +Each time we find a function under contract or a contract harness, we include it in the metadata.

+

Rationale and alternatives

+

Users of Kani may have many questions about their project--not only where their contracts and harnesses are, but also where their stubs are, what kinds of contracts they have, etc. Rather than try to answer every question a user might have, which would make the output quite verbose, we focus on these four:

+
    +
  1. Where are the harnesses?
  2. +
  3. Where are the contracts?
  4. +
  5. Which contracts are verified, and by which harnesses?
  6. +
  7. How many harnesses and functions under contract are there?
  8. +
+

We believe these questions are the most important for our use cases of tracking verification progress for customers and the standard library. The UX is designed to answer these questions clearly and concisely.

+

We could have a more verbose or granular output, e.g., printing the metadata on a per-crate or per-module level, or including stubs or other attributes. Such a design would have the benefit of providing more information, with the disadvantage of being more complex to implement and more information for the user to process. +If we do not implement this feature, users will have to obtain this metadata through manual searching, or by writing a script to do it themselves. This feature will improve our internal productivity by automating the process.

+

The Contracts table is close to Markdown, but not quite Markdown--it includes line separators between each row, when Markdown would only have a separator for the header. +We include the separator because without it, it can be difficult to tell from reading the terminal output which entries are in the same row. +The user can transform the table to Markdown by deleting these separators, and we can trivially add a Markdown option in the future if there is demand for it.

+

Open questions

+
    +
  1. Do we want to include more contracts information? We could print more granular information about contracts, e.g., the text of the contracts or the number of contracts.
  2. +
  3. More generally, we could introduce additional options that collect information about other Kani attributes (e.g., stubs). The default would be to leave them out, but this way a user could get more verbose output if they so choose.
  4. +
  5. Do we want to add a filtering option? For instance, --harnesses <pattern> and --contracts <pattern>, where pattern corresponds to a Rust-style path. For example, kani list --harnesses "my_crate::my_module::*" would include all harnesses with that path prefix, while kani list --contracts "my_crate::my_module::*" would include all functions under contract with that path prefix. (If we do this work, we could use it to improve our --harness pattern handling for verification).
  6. +
+

Out of scope / Future Improvements

+

It would be nice to differentiate between regular Kani harnesses and Bolero harnesses. Bolero harnesses invoke Kani using conditional compilation, e.g.:

+
#[cfg_attr(kani, kani::proof)]
+fn check() {
+    bolero::check!()...
+}
+
+

See this blog post for more information.

+

There's no easy way for us to know whether a harness comes from Bolero, since Bolero takes care of rewriting the test to use Kani syntax and invoking the Kani engine. By the time the harness gets to Kani, there's no way for us to tell it apart from a regular harness. Fixing this would require some changes to our Bolero integration.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + diff --git a/rfc/searcher.js b/rfc/searcher.js new file mode 100644 index 000000000000..d2b0aeed387b --- /dev/null +++ b/rfc/searcher.js @@ -0,0 +1,483 @@ +"use strict"; +window.search = window.search || {}; +(function search(search) { + // Search functionality + // + // You can use !hasFocus() to prevent keyhandling in your key + // event handlers while the user is typing their search. + + if (!Mark || !elasticlunr) { + return; + } + + //IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith + if (!String.prototype.startsWith) { + String.prototype.startsWith = function(search, pos) { + return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; + }; + } + + var search_wrap = document.getElementById('search-wrapper'), + searchbar = document.getElementById('searchbar'), + searchbar_outer = document.getElementById('searchbar-outer'), + searchresults = document.getElementById('searchresults'), + searchresults_outer = document.getElementById('searchresults-outer'), + searchresults_header = document.getElementById('searchresults-header'), + searchicon = document.getElementById('search-toggle'), + content = document.getElementById('content'), + + searchindex = null, + doc_urls = [], + results_options = { + teaser_word_count: 30, + limit_results: 30, + }, + search_options = { + bool: "AND", + expand: true, + fields: { + title: {boost: 1}, + body: {boost: 1}, + breadcrumbs: {boost: 0} + } + }, + mark_exclude = [], + marker = new Mark(content), + current_searchterm = "", + URL_SEARCH_PARAM = 'search', + URL_MARK_PARAM = 'highlight', + teaser_count = 0, + + SEARCH_HOTKEY_KEYCODE = 83, + ESCAPE_KEYCODE = 27, + DOWN_KEYCODE = 40, + UP_KEYCODE = 38, + SELECT_KEYCODE = 13; + + function hasFocus() { + return searchbar === document.activeElement; + } + + function removeChildren(elem) { + while (elem.firstChild) { + elem.removeChild(elem.firstChild); + } + } + + // Helper to parse a url into its building blocks. + function parseURL(url) { + var a = document.createElement('a'); + a.href = url; + return { + source: url, + protocol: a.protocol.replace(':',''), + host: a.hostname, + port: a.port, + params: (function(){ + var ret = {}; + var seg = a.search.replace(/^\?/,'').split('&'); + var len = seg.length, i = 0, s; + for (;i': '>', + '"': '"', + "'": ''' + }; + var repl = function(c) { return MAP[c]; }; + return function(s) { + return s.replace(/[&<>'"]/g, repl); + }; + })(); + + function formatSearchMetric(count, searchterm) { + if (count == 1) { + return count + " search result for '" + searchterm + "':"; + } else if (count == 0) { + return "No search results for '" + searchterm + "'."; + } else { + return count + " search results for '" + searchterm + "':"; + } + } + + function formatSearchResult(result, searchterms) { + var teaser = makeTeaser(escapeHTML(result.doc.body), searchterms); + teaser_count++; + + // The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor + var url = doc_urls[result.ref].split("#"); + if (url.length == 1) { // no anchor found + url.push(""); + } + + // encodeURIComponent escapes all chars that could allow an XSS except + // for '. Due to that we also manually replace ' with its url-encoded + // representation (%27). + var searchterms = encodeURIComponent(searchterms.join(" ")).replace(/\'/g, "%27"); + + return '' + result.doc.breadcrumbs + '' + + '' + + teaser + ''; + } + + function makeTeaser(body, searchterms) { + // The strategy is as follows: + // First, assign a value to each word in the document: + // Words that correspond to search terms (stemmer aware): 40 + // Normal words: 2 + // First word in a sentence: 8 + // Then use a sliding window with a constant number of words and count the + // sum of the values of the words within the window. Then use the window that got the + // maximum sum. If there are multiple maximas, then get the last one. + // Enclose the terms in . + var stemmed_searchterms = searchterms.map(function(w) { + return elasticlunr.stemmer(w.toLowerCase()); + }); + var searchterm_weight = 40; + var weighted = []; // contains elements of ["word", weight, index_in_document] + // split in sentences, then words + var sentences = body.toLowerCase().split('. '); + var index = 0; + var value = 0; + var searchterm_found = false; + for (var sentenceindex in sentences) { + var words = sentences[sentenceindex].split(' '); + value = 8; + for (var wordindex in words) { + var word = words[wordindex]; + if (word.length > 0) { + for (var searchtermindex in stemmed_searchterms) { + if (elasticlunr.stemmer(word).startsWith(stemmed_searchterms[searchtermindex])) { + value = searchterm_weight; + searchterm_found = true; + } + }; + weighted.push([word, value, index]); + value = 2; + } + index += word.length; + index += 1; // ' ' or '.' if last word in sentence + }; + index += 1; // because we split at a two-char boundary '. ' + }; + + if (weighted.length == 0) { + return body; + } + + var window_weight = []; + var window_size = Math.min(weighted.length, results_options.teaser_word_count); + + var cur_sum = 0; + for (var wordindex = 0; wordindex < window_size; wordindex++) { + cur_sum += weighted[wordindex][1]; + }; + window_weight.push(cur_sum); + for (var wordindex = 0; wordindex < weighted.length - window_size; wordindex++) { + cur_sum -= weighted[wordindex][1]; + cur_sum += weighted[wordindex + window_size][1]; + window_weight.push(cur_sum); + }; + + if (searchterm_found) { + var max_sum = 0; + var max_sum_window_index = 0; + // backwards + for (var i = window_weight.length - 1; i >= 0; i--) { + if (window_weight[i] > max_sum) { + max_sum = window_weight[i]; + max_sum_window_index = i; + } + }; + } else { + max_sum_window_index = 0; + } + + // add around searchterms + var teaser_split = []; + var index = weighted[max_sum_window_index][2]; + for (var i = max_sum_window_index; i < max_sum_window_index+window_size; i++) { + var word = weighted[i]; + if (index < word[2]) { + // missing text from index to start of `word` + teaser_split.push(body.substring(index, word[2])); + index = word[2]; + } + if (word[1] == searchterm_weight) { + teaser_split.push("") + } + index = word[2] + word[0].length; + teaser_split.push(body.substring(word[2], index)); + if (word[1] == searchterm_weight) { + teaser_split.push("") + } + }; + + return teaser_split.join(''); + } + + function init(config) { + results_options = config.results_options; + search_options = config.search_options; + searchbar_outer = config.searchbar_outer; + doc_urls = config.doc_urls; + searchindex = elasticlunr.Index.load(config.index); + + // Set up events + searchicon.addEventListener('click', function(e) { searchIconClickHandler(); }, false); + searchbar.addEventListener('keyup', function(e) { searchbarKeyUpHandler(); }, false); + document.addEventListener('keydown', function(e) { globalKeyHandler(e); }, false); + // If the user uses the browser buttons, do the same as if a reload happened + window.onpopstate = function(e) { doSearchOrMarkFromUrl(); }; + // Suppress "submit" events so the page doesn't reload when the user presses Enter + document.addEventListener('submit', function(e) { e.preventDefault(); }, false); + + // If reloaded, do the search or mark again, depending on the current url parameters + doSearchOrMarkFromUrl(); + } + + function unfocusSearchbar() { + // hacky, but just focusing a div only works once + var tmp = document.createElement('input'); + tmp.setAttribute('style', 'position: absolute; opacity: 0;'); + searchicon.appendChild(tmp); + tmp.focus(); + tmp.remove(); + } + + // On reload or browser history backwards/forwards events, parse the url and do search or mark + function doSearchOrMarkFromUrl() { + // Check current URL for search request + var url = parseURL(window.location.href); + if (url.params.hasOwnProperty(URL_SEARCH_PARAM) + && url.params[URL_SEARCH_PARAM] != "") { + showSearch(true); + searchbar.value = decodeURIComponent( + (url.params[URL_SEARCH_PARAM]+'').replace(/\+/g, '%20')); + searchbarKeyUpHandler(); // -> doSearch() + } else { + showSearch(false); + } + + if (url.params.hasOwnProperty(URL_MARK_PARAM)) { + var words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' '); + marker.mark(words, { + exclude: mark_exclude + }); + + var markers = document.querySelectorAll("mark"); + function hide() { + for (var i = 0; i < markers.length; i++) { + markers[i].classList.add("fade-out"); + window.setTimeout(function(e) { marker.unmark(); }, 300); + } + } + for (var i = 0; i < markers.length; i++) { + markers[i].addEventListener('click', hide); + } + } + } + + // Eventhandler for keyevents on `document` + function globalKeyHandler(e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text') { return; } + + if (e.keyCode === ESCAPE_KEYCODE) { + e.preventDefault(); + searchbar.classList.remove("active"); + setSearchUrlParameters("", + (searchbar.value.trim() !== "") ? "push" : "replace"); + if (hasFocus()) { + unfocusSearchbar(); + } + showSearch(false); + marker.unmark(); + } else if (!hasFocus() && e.keyCode === SEARCH_HOTKEY_KEYCODE) { + e.preventDefault(); + showSearch(true); + window.scrollTo(0, 0); + searchbar.select(); + } else if (hasFocus() && e.keyCode === DOWN_KEYCODE) { + e.preventDefault(); + unfocusSearchbar(); + searchresults.firstElementChild.classList.add("focus"); + } else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE + || e.keyCode === UP_KEYCODE + || e.keyCode === SELECT_KEYCODE)) { + // not `:focus` because browser does annoying scrolling + var focused = searchresults.querySelector("li.focus"); + if (!focused) return; + e.preventDefault(); + if (e.keyCode === DOWN_KEYCODE) { + var next = focused.nextElementSibling; + if (next) { + focused.classList.remove("focus"); + next.classList.add("focus"); + } + } else if (e.keyCode === UP_KEYCODE) { + focused.classList.remove("focus"); + var prev = focused.previousElementSibling; + if (prev) { + prev.classList.add("focus"); + } else { + searchbar.select(); + } + } else { // SELECT_KEYCODE + window.location.assign(focused.querySelector('a')); + } + } + } + + function showSearch(yes) { + if (yes) { + search_wrap.classList.remove('hidden'); + searchicon.setAttribute('aria-expanded', 'true'); + } else { + search_wrap.classList.add('hidden'); + searchicon.setAttribute('aria-expanded', 'false'); + var results = searchresults.children; + for (var i = 0; i < results.length; i++) { + results[i].classList.remove("focus"); + } + } + } + + function showResults(yes) { + if (yes) { + searchresults_outer.classList.remove('hidden'); + } else { + searchresults_outer.classList.add('hidden'); + } + } + + // Eventhandler for search icon + function searchIconClickHandler() { + if (search_wrap.classList.contains('hidden')) { + showSearch(true); + window.scrollTo(0, 0); + searchbar.select(); + } else { + showSearch(false); + } + } + + // Eventhandler for keyevents while the searchbar is focused + function searchbarKeyUpHandler() { + var searchterm = searchbar.value.trim(); + if (searchterm != "") { + searchbar.classList.add("active"); + doSearch(searchterm); + } else { + searchbar.classList.remove("active"); + showResults(false); + removeChildren(searchresults); + } + + setSearchUrlParameters(searchterm, "push_if_new_search_else_replace"); + + // Remove marks + marker.unmark(); + } + + // Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and #heading-anchor . + // `action` can be one of "push", "replace", "push_if_new_search_else_replace" + // and replaces or pushes a new browser history item. + // "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet. + function setSearchUrlParameters(searchterm, action) { + var url = parseURL(window.location.href); + var first_search = ! url.params.hasOwnProperty(URL_SEARCH_PARAM); + if (searchterm != "" || action == "push_if_new_search_else_replace") { + url.params[URL_SEARCH_PARAM] = searchterm; + delete url.params[URL_MARK_PARAM]; + url.hash = ""; + } else { + delete url.params[URL_MARK_PARAM]; + delete url.params[URL_SEARCH_PARAM]; + } + // A new search will also add a new history item, so the user can go back + // to the page prior to searching. A updated search term will only replace + // the url. + if (action == "push" || (action == "push_if_new_search_else_replace" && first_search) ) { + history.pushState({}, document.title, renderURL(url)); + } else if (action == "replace" || (action == "push_if_new_search_else_replace" && !first_search) ) { + history.replaceState({}, document.title, renderURL(url)); + } + } + + function doSearch(searchterm) { + + // Don't search the same twice + if (current_searchterm == searchterm) { return; } + else { current_searchterm = searchterm; } + + if (searchindex == null) { return; } + + // Do the actual search + var results = searchindex.search(searchterm, search_options); + var resultcount = Math.min(results.length, results_options.limit_results); + + // Display search metrics + searchresults_header.innerText = formatSearchMetric(resultcount, searchterm); + + // Clear and insert results + var searchterms = searchterm.split(' '); + removeChildren(searchresults); + for(var i = 0; i < resultcount ; i++){ + var resultElem = document.createElement('li'); + resultElem.innerHTML = formatSearchResult(results[i], searchterms); + searchresults.appendChild(resultElem); + } + + // Display results + showResults(true); + } + + fetch(path_to_root + 'searchindex.json') + .then(response => response.json()) + .then(json => init(json)) + .catch(error => { // Try to load searchindex.js if fetch failed + var script = document.createElement('script'); + script.src = path_to_root + 'searchindex.js'; + script.onload = () => init(window.search); + document.head.appendChild(script); + }); + + // Exported functions + search.hasFocus = hasFocus; +})(window.search); diff --git a/rfc/searchindex.js b/rfc/searchindex.js new file mode 100644 index 000000000000..c19cf9cc8e32 --- /dev/null +++ b/rfc/searchindex.js @@ -0,0 +1 @@ +Object.assign(window.search, {"doc_urls":["intro.html#introduction","intro.html#when-to-create-an-rfc","intro.html#the-rfc-process","template.html#summary","template.html#user-impact","template.html#user-experience","template.html#software-design","template.html#rationale-and-alternatives","template.html#open-questions","template.html#out-of-scope--future-improvements","rfcs/0001-mir-linker.html#summary","rfcs/0001-mir-linker.html#user-impact","rfcs/0001-mir-linker.html#user-experience","rfcs/0001-mir-linker.html#detailed-design","rfcs/0001-mir-linker.html#kanis-sysroot","rfcs/0001-mir-linker.html#cross-crate-reachability-analysis","rfcs/0001-mir-linker.html#dependencies-vs-target-crate-compilation","rfcs/0001-mir-linker.html#rational-and-alternatives","rfcs/0001-mir-linker.html#benefits","rfcs/0001-mir-linker.html#risks","rfcs/0001-mir-linker.html#alternatives","rfcs/0001-mir-linker.html#open-questions","rfcs/0001-mir-linker.html#future-possibilities","rfcs/0002-function-stubbing.html#summary","rfcs/0002-function-stubbing.html#scope","rfcs/0002-function-stubbing.html#user-impact","rfcs/0002-function-stubbing.html#mocking-randomization","rfcs/0002-function-stubbing.html#user-experience","rfcs/0002-function-stubbing.html#stub-sets","rfcs/0002-function-stubbing.html#error-conditions","rfcs/0002-function-stubbing.html#stub-compatibility-and-validation","rfcs/0002-function-stubbing.html#pedagogy","rfcs/0002-function-stubbing.html#detailed-design","rfcs/0002-function-stubbing.html#rationale-and-alternatives-user-experience","rfcs/0002-function-stubbing.html#benefits","rfcs/0002-function-stubbing.html#risks","rfcs/0002-function-stubbing.html#comparison-to-function-contracts","rfcs/0002-function-stubbing.html#alternative-1-annotate-stubbed-functions","rfcs/0002-function-stubbing.html#alternative-2-annotate-stubs","rfcs/0002-function-stubbing.html#alternative-3-annotate-harnesses-and-stubs","rfcs/0002-function-stubbing.html#alternative-4-specify-stubs-in-a-file","rfcs/0002-function-stubbing.html#rationale-and-alternatives-stubbing-mechanism","rfcs/0002-function-stubbing.html#alternative-1-conditional-compilation","rfcs/0002-function-stubbing.html#alternative-2-source-to-source-transformation","rfcs/0002-function-stubbing.html#alternative-3-ast-to-ast-or-hir-to-hir-transformation","rfcs/0002-function-stubbing.html#open-questions","rfcs/0002-function-stubbing.html#limitations","rfcs/0002-function-stubbing.html#future-possibilities","rfcs/0002-function-stubbing.html#appendix-an-extended-example","rfcs/0003-cover-statement.html#summary","rfcs/0003-cover-statement.html#user-impact","rfcs/0003-cover-statement.html#user-experience","rfcs/0003-cover-statement.html#impact-on-overall-verification-status","rfcs/0003-cover-statement.html#inclusion-in-the-verification-summary","rfcs/0003-cover-statement.html#interaction-with-other-checks","rfcs/0003-cover-statement.html#detailed-design","rfcs/0003-cover-statement.html#rationale-and-alternatives","rfcs/0003-cover-statement.html#open-questions","rfcs/0003-cover-statement.html#other-considerations","rfcs/0003-cover-statement.html#future-possibilities","rfcs/0004-loop-contract-synthesis.html#summary","rfcs/0004-loop-contract-synthesis.html#user-impact","rfcs/0004-loop-contract-synthesis.html#user-experience","rfcs/0004-loop-contract-synthesis.html#detailed-design","rfcs/0004-loop-contract-synthesis.html#rationale-and-alternatives","rfcs/0004-loop-contract-synthesis.html#open-questions","rfcs/0004-loop-contract-synthesis.html#future-possibilities","rfcs/0005-should-panic-attr.html#summary","rfcs/0005-should-panic-attr.html#user-impact","rfcs/0005-should-panic-attr.html#user-experience","rfcs/0005-should-panic-attr.html#single-harness","rfcs/0005-should-panic-attr.html#multiple-harnesses","rfcs/0005-should-panic-attr.html#multiple-panics","rfcs/0005-should-panic-attr.html#availability","rfcs/0005-should-panic-attr.html#pedagogy","rfcs/0005-should-panic-attr.html#detailed-design","rfcs/0005-should-panic-attr.html#rationale-and-alternatives","rfcs/0005-should-panic-attr.html#alternative-1-generic-failures","rfcs/0005-should-panic-attr.html#alternative-2-name","rfcs/0005-should-panic-attr.html#alternative-3-the-expected-argument","rfcs/0005-should-panic-attr.html#alternative-4-granularity","rfcs/0005-should-panic-attr.html#alternative-5-kani-api","rfcs/0005-should-panic-attr.html#open-questions","rfcs/0005-should-panic-attr.html#resolved-questions","rfcs/0005-should-panic-attr.html#future-possibilities","rfcs/0006-unstable-api.html#summary","rfcs/0006-unstable-api.html#user-impact","rfcs/0006-unstable-api.html#user-experience","rfcs/0006-unstable-api.html#detailed-design","rfcs/0006-unstable-api.html#api-stabilization","rfcs/0006-unstable-api.html#api-removal","rfcs/0006-unstable-api.html#rational-and-alternatives","rfcs/0006-unstable-api.html#open-questions","rfcs/0006-unstable-api.html#future-possibilities","rfcs/0007-global-conditions.html#summary","rfcs/0007-global-conditions.html#user-impact","rfcs/0007-global-conditions.html#user-experience","rfcs/0007-global-conditions.html#detailed-design","rfcs/0007-global-conditions.html#rationale-and-alternatives","rfcs/0007-global-conditions.html#alternative-global-conditions-as-regular-checks","rfcs/0007-global-conditions.html#open-questions","rfcs/0007-global-conditions.html#future-possibilities","rfcs/0008-line-coverage.html#summary","rfcs/0008-line-coverage.html#user-impact","rfcs/0008-line-coverage.html#user-experience","rfcs/0008-line-coverage.html#high-level-changes","rfcs/0008-line-coverage.html#experimental-output-format-for-coverage-results","rfcs/0008-line-coverage.html#detailed-design","rfcs/0008-line-coverage.html#architecture","rfcs/0008-line-coverage.html#coverage-checks","rfcs/0008-line-coverage.html#rationale-and-alternatives","rfcs/0008-line-coverage.html#benefits-from-a-native-coverage-solution","rfcs/0008-line-coverage.html#open-questions","rfcs/0008-line-coverage.html#future-possibilities","rfcs/0009-function-contracts.html#summary","rfcs/0009-function-contracts.html#user-impact","rfcs/0009-function-contracts.html#user-experience","rfcs/0009-function-contracts.html#write-sets-and-havocking","rfcs/0009-function-contracts.html#history-expressions","rfcs/0009-function-contracts.html#workflow-and-attribute-constraints-overview","rfcs/0009-function-contracts.html#detailed-design","rfcs/0009-function-contracts.html#code-generation-in-kani_macros","rfcs/0009-function-contracts.html#recursion","rfcs/0009-function-contracts.html#changes-to-other-components","rfcs/0009-function-contracts.html#rationale-and-alternatives","rfcs/0009-function-contracts.html#kani-side-implementation-vs-cbmc","rfcs/0009-function-contracts.html#expanding-all-contract-macros-at-the-same-time","rfcs/0009-function-contracts.html#generating-nested-functions-instead-of-siblings","rfcs/0009-function-contracts.html#explicit-command-line-checkingsubstitution-vs-attributes","rfcs/0009-function-contracts.html#polymorphism-during-contract-checking","rfcs/0009-function-contracts.html#user-supplied-harnesses","rfcs/0009-function-contracts.html#open-questions","rfcs/0009-function-contracts.html#future-possibilities","rfcs/0010-quantifiers.html#summary","rfcs/0010-quantifiers.html#user-impact","rfcs/0010-quantifiers.html#user-experience","rfcs/0010-quantifiers.html#detailed-design","rfcs/0010-quantifiers.html#open-questions","rfcs/0010-quantifiers.html#future-possibilities","rfcs/0011-source-coverage.html#summary","rfcs/0011-source-coverage.html#user-impact","rfcs/0011-source-coverage.html#user-experience","rfcs/0011-source-coverage.html#the-llvm-code-coverage-workflow","rfcs/0011-source-coverage.html#the-kani-coverage-workflow","rfcs/0011-source-coverage.html#detailed-design","rfcs/0011-source-coverage.html#the-rust-coverage-instrumentation","rfcs/0011-source-coverage.html#the-default-coverage-workflow-in-kani","rfcs/0011-source-coverage.html#rationale-and-alternatives","rfcs/0011-source-coverage.html#other-coverage-implementations","rfcs/0011-source-coverage.html#open-questions","rfcs/0011-source-coverage.html#future-possibilities","rfcs/0011-source-coverage.html#integration-with-llvm","rfcs/0011-source-coverage.html#optimization-with-coverage-counter-expressions","rfcs/0012-loop-contracts.html#summary","rfcs/0012-loop-contracts.html#user-impact","rfcs/0012-loop-contracts.html#user-experience","rfcs/0012-loop-contracts.html#write-sets-and-havocking","rfcs/0012-loop-contracts.html#proof-of-termination","rfcs/0012-loop-contracts.html#detailed-design","rfcs/0012-loop-contracts.html#procedural-macros-loop_invariant-loop_modifies-and-loop_decreases","rfcs/0012-loop-contracts.html#code-generation-for-builtin-functions","rfcs/0012-loop-contracts.html#goto-level-havocing","rfcs/0012-loop-contracts.html#rationale-and-alternatives","rfcs/0012-loop-contracts.html#rust-level-transformation-vs-cbmc","rfcs/0012-loop-contracts.html#open-questions","rfcs/0012-loop-contracts.html#future-possibilities","rfcs/0013-list.html#summary","rfcs/0013-list.html#user-impact","rfcs/0013-list.html#user-experience","rfcs/0013-list.html#pretty-format","rfcs/0013-list.html#json-format","rfcs/0013-list.html#software-design","rfcs/0013-list.html#drivermetdata-changes","rfcs/0013-list.html#compiler-changes","rfcs/0013-list.html#rationale-and-alternatives","rfcs/0013-list.html#open-questions","rfcs/0013-list.html#out-of-scope--future-improvements"],"index":{"documentStore":{"docInfo":{"0":{"body":27,"breadcrumbs":2,"title":1},"1":{"body":76,"breadcrumbs":3,"title":2},"10":{"body":33,"breadcrumbs":4,"title":1},"100":{"body":2,"breadcrumbs":5,"title":2},"101":{"body":107,"breadcrumbs":5,"title":2},"102":{"body":39,"breadcrumbs":4,"title":1},"103":{"body":150,"breadcrumbs":5,"title":2},"104":{"body":30,"breadcrumbs":5,"title":2},"105":{"body":34,"breadcrumbs":6,"title":3},"106":{"body":64,"breadcrumbs":8,"title":5},"107":{"body":0,"breadcrumbs":5,"title":2},"108":{"body":53,"breadcrumbs":4,"title":1},"109":{"body":129,"breadcrumbs":5,"title":2},"11":{"body":105,"breadcrumbs":5,"title":2},"110":{"body":0,"breadcrumbs":5,"title":2},"111":{"body":269,"breadcrumbs":7,"title":4},"112":{"body":110,"breadcrumbs":5,"title":2},"113":{"body":95,"breadcrumbs":5,"title":2},"114":{"body":49,"breadcrumbs":4,"title":1},"115":{"body":87,"breadcrumbs":5,"title":2},"116":{"body":452,"breadcrumbs":5,"title":2},"117":{"body":375,"breadcrumbs":6,"title":3},"118":{"body":157,"breadcrumbs":5,"title":2},"119":{"body":173,"breadcrumbs":7,"title":4},"12":{"body":44,"breadcrumbs":5,"title":2},"120":{"body":42,"breadcrumbs":5,"title":2},"121":{"body":266,"breadcrumbs":6,"title":3},"122":{"body":105,"breadcrumbs":4,"title":1},"123":{"body":113,"breadcrumbs":5,"title":2},"124":{"body":248,"breadcrumbs":5,"title":2},"125":{"body":80,"breadcrumbs":8,"title":5},"126":{"body":49,"breadcrumbs":8,"title":5},"127":{"body":73,"breadcrumbs":8,"title":5},"128":{"body":85,"breadcrumbs":9,"title":6},"129":{"body":41,"breadcrumbs":7,"title":4},"13":{"body":164,"breadcrumbs":5,"title":2},"130":{"body":85,"breadcrumbs":6,"title":3},"131":{"body":379,"breadcrumbs":5,"title":2},"132":{"body":715,"breadcrumbs":5,"title":2},"133":{"body":27,"breadcrumbs":3,"title":1},"134":{"body":122,"breadcrumbs":4,"title":2},"135":{"body":369,"breadcrumbs":4,"title":2},"136":{"body":9,"breadcrumbs":4,"title":2},"137":{"body":53,"breadcrumbs":4,"title":2},"138":{"body":15,"breadcrumbs":4,"title":2},"139":{"body":42,"breadcrumbs":4,"title":1},"14":{"body":85,"breadcrumbs":5,"title":2},"140":{"body":151,"breadcrumbs":5,"title":2},"141":{"body":15,"breadcrumbs":5,"title":2},"142":{"body":278,"breadcrumbs":7,"title":4},"143":{"body":582,"breadcrumbs":6,"title":3},"144":{"body":54,"breadcrumbs":5,"title":2},"145":{"body":691,"breadcrumbs":6,"title":3},"146":{"body":213,"breadcrumbs":7,"title":4},"147":{"body":0,"breadcrumbs":5,"title":2},"148":{"body":53,"breadcrumbs":5,"title":2},"149":{"body":31,"breadcrumbs":5,"title":2},"15":{"body":146,"breadcrumbs":7,"title":4},"150":{"body":0,"breadcrumbs":5,"title":2},"151":{"body":250,"breadcrumbs":5,"title":2},"152":{"body":209,"breadcrumbs":7,"title":4},"153":{"body":50,"breadcrumbs":4,"title":1},"154":{"body":85,"breadcrumbs":5,"title":2},"155":{"body":240,"breadcrumbs":5,"title":2},"156":{"body":113,"breadcrumbs":6,"title":3},"157":{"body":98,"breadcrumbs":5,"title":2},"158":{"body":30,"breadcrumbs":5,"title":2},"159":{"body":17,"breadcrumbs":8,"title":5},"16":{"body":46,"breadcrumbs":8,"title":5},"160":{"body":31,"breadcrumbs":7,"title":4},"161":{"body":50,"breadcrumbs":6,"title":3},"162":{"body":0,"breadcrumbs":5,"title":2},"163":{"body":61,"breadcrumbs":8,"title":5},"164":{"body":80,"breadcrumbs":5,"title":2},"165":{"body":13,"breadcrumbs":5,"title":2},"166":{"body":27,"breadcrumbs":3,"title":1},"167":{"body":75,"breadcrumbs":4,"title":2},"168":{"body":63,"breadcrumbs":4,"title":2},"169":{"body":90,"breadcrumbs":4,"title":2},"17":{"body":8,"breadcrumbs":5,"title":2},"170":{"body":123,"breadcrumbs":4,"title":2},"171":{"body":0,"breadcrumbs":4,"title":2},"172":{"body":27,"breadcrumbs":4,"title":2},"173":{"body":24,"breadcrumbs":4,"title":2},"174":{"body":133,"breadcrumbs":4,"title":2},"175":{"body":75,"breadcrumbs":4,"title":2},"176":{"body":60,"breadcrumbs":6,"title":4},"18":{"body":34,"breadcrumbs":4,"title":1},"19":{"body":94,"breadcrumbs":4,"title":1},"2":{"body":240,"breadcrumbs":3,"title":2},"20":{"body":211,"breadcrumbs":4,"title":1},"21":{"body":70,"breadcrumbs":5,"title":2},"22":{"body":58,"breadcrumbs":5,"title":2},"23":{"body":36,"breadcrumbs":4,"title":1},"24":{"body":30,"breadcrumbs":4,"title":1},"25":{"body":176,"breadcrumbs":5,"title":2},"26":{"body":84,"breadcrumbs":5,"title":2},"27":{"body":208,"breadcrumbs":5,"title":2},"28":{"body":56,"breadcrumbs":5,"title":2},"29":{"body":40,"breadcrumbs":5,"title":2},"3":{"body":61,"breadcrumbs":3,"title":1},"30":{"body":201,"breadcrumbs":6,"title":3},"31":{"body":24,"breadcrumbs":4,"title":1},"32":{"body":133,"breadcrumbs":5,"title":2},"33":{"body":12,"breadcrumbs":7,"title":4},"34":{"body":59,"breadcrumbs":4,"title":1},"35":{"body":25,"breadcrumbs":4,"title":1},"36":{"body":110,"breadcrumbs":6,"title":3},"37":{"body":40,"breadcrumbs":8,"title":5},"38":{"body":39,"breadcrumbs":7,"title":4},"39":{"body":119,"breadcrumbs":8,"title":5},"4":{"body":43,"breadcrumbs":4,"title":2},"40":{"body":60,"breadcrumbs":8,"title":5},"41":{"body":117,"breadcrumbs":7,"title":4},"42":{"body":49,"breadcrumbs":7,"title":4},"43":{"body":80,"breadcrumbs":8,"title":5},"44":{"body":112,"breadcrumbs":10,"title":7},"45":{"body":31,"breadcrumbs":5,"title":2},"46":{"body":13,"breadcrumbs":4,"title":1},"47":{"body":130,"breadcrumbs":5,"title":2},"48":{"body":197,"breadcrumbs":6,"title":3},"49":{"body":31,"breadcrumbs":4,"title":1},"5":{"body":73,"breadcrumbs":4,"title":2},"50":{"body":108,"breadcrumbs":5,"title":2},"51":{"body":73,"breadcrumbs":5,"title":2},"52":{"body":105,"breadcrumbs":7,"title":4},"53":{"body":39,"breadcrumbs":6,"title":3},"54":{"body":21,"breadcrumbs":5,"title":2},"55":{"body":86,"breadcrumbs":5,"title":2},"56":{"body":78,"breadcrumbs":5,"title":2},"57":{"body":46,"breadcrumbs":5,"title":2},"58":{"body":11,"breadcrumbs":4,"title":1},"59":{"body":21,"breadcrumbs":5,"title":2},"6":{"body":53,"breadcrumbs":4,"title":2},"60":{"body":40,"breadcrumbs":5,"title":1},"61":{"body":318,"breadcrumbs":6,"title":2},"62":{"body":136,"breadcrumbs":6,"title":2},"63":{"body":317,"breadcrumbs":6,"title":2},"64":{"body":72,"breadcrumbs":6,"title":2},"65":{"body":165,"breadcrumbs":6,"title":2},"66":{"body":108,"breadcrumbs":6,"title":2},"67":{"body":44,"breadcrumbs":4,"title":1},"68":{"body":134,"breadcrumbs":5,"title":2},"69":{"body":13,"breadcrumbs":5,"title":2},"7":{"body":13,"breadcrumbs":4,"title":2},"70":{"body":235,"breadcrumbs":5,"title":2},"71":{"body":74,"breadcrumbs":5,"title":2},"72":{"body":76,"breadcrumbs":5,"title":2},"73":{"body":37,"breadcrumbs":4,"title":1},"74":{"body":56,"breadcrumbs":4,"title":1},"75":{"body":72,"breadcrumbs":5,"title":2},"76":{"body":19,"breadcrumbs":5,"title":2},"77":{"body":24,"breadcrumbs":7,"title":4},"78":{"body":49,"breadcrumbs":6,"title":3},"79":{"body":76,"breadcrumbs":7,"title":4},"8":{"body":37,"breadcrumbs":4,"title":2},"80":{"body":47,"breadcrumbs":6,"title":3},"81":{"body":29,"breadcrumbs":7,"title":4},"82":{"body":24,"breadcrumbs":5,"title":2},"83":{"body":34,"breadcrumbs":5,"title":2},"84":{"body":33,"breadcrumbs":5,"title":2},"85":{"body":32,"breadcrumbs":4,"title":1},"86":{"body":141,"breadcrumbs":5,"title":2},"87":{"body":96,"breadcrumbs":5,"title":2},"88":{"body":43,"breadcrumbs":5,"title":2},"89":{"body":20,"breadcrumbs":5,"title":2},"9":{"body":50,"breadcrumbs":6,"title":4},"90":{"body":21,"breadcrumbs":5,"title":2},"91":{"body":57,"breadcrumbs":5,"title":2},"92":{"body":6,"breadcrumbs":5,"title":2},"93":{"body":18,"breadcrumbs":5,"title":2},"94":{"body":39,"breadcrumbs":4,"title":1},"95":{"body":92,"breadcrumbs":5,"title":2},"96":{"body":176,"breadcrumbs":5,"title":2},"97":{"body":25,"breadcrumbs":5,"title":2},"98":{"body":53,"breadcrumbs":5,"title":2},"99":{"body":27,"breadcrumbs":8,"title":5}},"docs":{"0":{"body":"Kani is an open-source verification tool that uses automated reasoning to analyze Rust programs. In order to integrate feedback from developers and users on future changes to Kani, we decided to follow a light-weight \"RFC\" (request for comments) process.","breadcrumbs":"Introduction » Introduction","id":"0","title":"Introduction"},"1":{"body":"You should create an RFC in one of two cases: The change you are proposing would be a \"one way door\": e.g. a major change to the public API, a new feature that would be difficult to modify once released, etc. The change you are making has a significant design component, and would benefit from a design review. Bugs and improvements to existing features do not require an RFC. If you are in doubt, feel free to create a feature request and discuss the next steps in the new issue. Your PR reviewer may also request an RFC if your change appears to fall into category 1 or 2. You do not necessarily need to create an RFC immediately. It is our experience that it is often best to write some \"proof of concept\" code to test out possible ideas before writing the formal RFC.","breadcrumbs":"Introduction » When to create an RFC","id":"1","title":"When to create an RFC"},"10":{"body":"Feature Name: MIR Linker (mir_linker) RFC Tracking Issue : https://github.com/model-checking/kani/issues/1588 RFC PR: https://github.com/model-checking/kani/pull/1600 Status: Stable Version: 3 Fix linking issues with the rust standard library in a scalable manner by only generating goto-program for code that is reachable from the user harnesses.","breadcrumbs":"0001-mir-linker » Summary","id":"10","title":"Summary"},"100":{"body":"No open questions.","breadcrumbs":"0007-global-conditions » Open questions","id":"100","title":"Open questions"},"101":{"body":"A redesign of Kani's output is likely to change the style/architecture to report global conditions. The results for global conditions would be computed during postprocessing based on the results of other checks. Global conditions' checks aren't part of the SAT, therefore this computation won't impact verification time. We do not discuss the specific interface to report the failed checks because it needs improvements (for both global conditions and standard verification). In particular, the description field is the only information printed for properties (such as cover statements) without trace locations. There are additional improvements we should consider: printing the actual status (for global conditions, this won't always be FAILED), avoid the repetition of Failed Checks: , etc. This comment discusses problems with the current interface on some examples. In other words, global conditions won't force a specific mechanism to be enabled. For example, if the #[kani::should_panic] attribute is converted into a global condition, it will continue to be enabled through the attribute itself. Other global conditions may be enabled through CLI flags only (e.g., --fail-uncoverable), or a combination of options in general.","breadcrumbs":"0007-global-conditions » Future possibilities","id":"101","title":"Future possibilities"},"102":{"body":"Feature Name: Line coverage (line-coverage) Feature Request Issue: https://github.com/model-checking/kani/issues/2610 RFC PR: https://github.com/model-checking/kani/pull/2609 Status: Cancelled Version: 0 Proof-of-concept: https://github.com/model-checking/kani/pull/2609 (Kani) + https://github.com/model-checking/kani-vscode-extension/pull/122 (Kani VS Code Extension) Add verification-based line coverage reports to Kani.","breadcrumbs":"0008-line-coverage » Summary","id":"102","title":"Summary"},"103":{"body":"Nowadays, users can't easily obtain verification-based coverage reports in Kani. Generally speaking, coverage reports show which parts of the code under verification are covered and which are not. Because of that, coverage is often seen as a great metric to determine the quality of a verification effort. Moreover, some users prefer using coverage information for harness development and debugging. That's because coverage information provides users with more familiar way to interpret verification results. This RFC proposes adding a new option for verification-based line coverage reports to Kani. As mentioned earlier, we expect users to employ this coverage-related option on several stages of a verification effort: Learning: New users are more familiar with coverage reports than property-based results. Development: Some users prefer coverage results to property-based results since they are easier to interpret. CI Integration : Users may want to enforce a minimum percentage of code coverage for new contributions. Debugging: Users may find coverage reports particularly helpful when inputs are over-constrained (missing some corner cases). Evaluation: Users can easily evaluate where and when more verification work is needed (some projects aim for 100% coverage). Moreover, adding this option directly to Kani, instead of relying on another tools, is likely to: Increase the speed of development Improve testing for coverage features Which translates into faster and more reliable coverage options for users.","breadcrumbs":"0008-line-coverage » User Impact","id":"103","title":"User Impact"},"104":{"body":"The goal is for Kani to generate code coverage report per harness in a well established format, such as LCOV , and possibly a summary in the output. For now, we will focus on an interim solution that will enable us to assess the results of our instrumentation and enable integration with the Kani VS Code extension.","breadcrumbs":"0008-line-coverage » User Experience","id":"104","title":"User Experience"},"105":{"body":"For the first version, this experimental feature will report verification results along coverage reports. Because of that, we'll add a new section Coverage results that shows coverage results for each individual harness. In the following, we describe an experimental output format. Note that the final output format and overall UX is to be determined.","breadcrumbs":"0008-line-coverage » High-level changes","id":"105","title":"High-level changes"},"106":{"body":"The Coverage results section for each harness will produce coverage information in a CSV format as follows: , , where is either FULL, PARTIAL or NONE. As mentioned, this format is designed for evaluating the native instrumentation-based design and is likely to be substituted with another well-established format as soon as possible. Users are not expected to consume this output directly. Instead, coverage data is to be consumed by the Kani VS Code extension and displayed as in the VS Code Extension prototype . How to activate and display coverage information in the extension is out of scope for this RFC. That said, a proof-of-concept implementation is available here .","breadcrumbs":"0008-line-coverage » Experimental output format for coverage results","id":"106","title":"Experimental output format for coverage results"},"107":{"body":"","breadcrumbs":"0008-line-coverage » Detailed Design","id":"107","title":"Detailed Design"},"108":{"body":"We will add a new unstable --coverage verification option to Kani which will require -Z line-coverage until this feature is stabilized. We will also add a new --coverage-checks option to kani-compiler, which will result in the injection of coverage checks before each Rust statement and terminator [1] . This option will be supplied by kani-driver when the --coverage option is selected. These options will cause Kani to inject coverage checks during compilation and postprocess them to produce the coverage results sections described earlier.","breadcrumbs":"0008-line-coverage » Architecture","id":"108","title":"Architecture"},"109":{"body":"Coverage checks are a new class of checks similar to cover checks . The main difference is that users cannot directly interact with coverage checks (i.e., they cannot add or remove them manually). Coverage checks are encoded as an assert(false) statement (to test reachability) with a fixed description. In addition, coverage checks are: Hidden from verification results. Postprocessed to produce coverage results. In the following, we describe the injection and postprocessing procedures to generate coverage results. Injection of Coverage Checks The injection of coverage checks will be done while generating code for basic blocks. This allows us to add one coverage check before each statement and terminator, which provides the most accurate results [1] . It's not completely clear how this compares to the coverage instrumentation done in the Rust compiler, but an exploration to use the compiler APIs revealed that they're quite similar [2] . Postprocessing Coverage Checks The injection of coverage checks often results in one or more checks per line (assuming a well-formatted program). We'll postprocess these checks so for each line if all checks are SATISFIED: return FULL if all checks are UNSATISFIED: return NONE otherwise: return PARTIAL We won't report coverage status for lines which don't include a coverage check.","breadcrumbs":"0008-line-coverage » Coverage Checks","id":"109","title":"Coverage Checks"},"11":{"body":"The main goal of this RFC is to enable Kani users to link against all supported constructs from the std library. Currently, Kani will only link to items that are either generic or have an inline annotation. The approach introduced in this RFC will have the following secondary benefits. Reduce spurious warnings about unsupported features for cases where the feature is not reachable from any harness. In the verification mode, we will likely see a reduction on the compilation time and memory consumption by pruning the inputs of symtab2gb and goto-instrument. Compared to linking against the standard library goto-models that take more than 5 GB. In a potential assessment mode, only analyze code that is reachable from all public items in the target crate. One downside is that we will include a pre-compiled version of the std, our release bundle will double in size (See Rational and Alternatives for more information on the size overhead). This will negatively impact the time taken to set up Kani (triggered by either the first time a user invokes kani | cargo-kani , or explicit invoke the subcommand setup).","breadcrumbs":"0001-mir-linker » User Impact","id":"11","title":"User Impact"},"110":{"body":"","breadcrumbs":"0008-line-coverage » Rationale and alternatives","id":"110","title":"Rationale and alternatives"},"111":{"body":"Kani has relied on cbmc-viewer to report coverage information since the beginning. In essence, cbmc-viewer consumes data from coverage-focused invocations of CBMC and produces an HTML report containing (1) coverage information and (2) counterexample traces. Recently, there have been some issues with the coverage information reported by cbmc-viewer (e.g., #2048 or #1707 ), forcing us to mark the --visualize option as unstable and disable coverage results in the reports (in #2206 ). However, it's possible for Kani to report coverage information without cbmc-viewer, as explained before. This would give Kani control on both ends: The instrumentation performed on the program. Eventually, this would allow us to report more precise coverage information (maybe similar to Rust's instrument-based code coverage ). The format of the coverage report to be generated. Similarly, this would allow us to generate coverage data in different formats (see #1706 for GCOV, or #1777 for LCOV). While technically this is also doable from cbmc-viewer's output, development is likely to be faster this way. Coverage through cbmc-viewer As an alternative, we could fix and use cbmc-viewer to report line coverage. Most of the issues with cbmc-viewer are generally due to: Missing locations due to non-propagation of locations in either Kani or CBMC. Differences in the definition of a basic block in CBMC and Rust's MIR. Scarce documentation for coverage-related options (i.e., --cover