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..72dbd8025918 --- /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..58e3c41f09e3 --- /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..90a04c141f70 --- /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..b885a911b4ed --- /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..c44f780ae710 --- /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..036a42104fc9 --- /dev/null +++ b/build-from-source.html @@ -0,0 +1,240 @@ + + + + + + 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. CBMC Viewer (latest release)
  6. +
  7. Kissat (Release 3.1.1)
  8. +
+

Kani has been tested in Ubuntu and macOS platforms.

+

Install dependencies on Ubuntu

+

Support is available for Ubuntu 18.04, 20.04 and 22.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:

+
cargo build-dev
+
+

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..b7617722e4ff --- /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..033f7e2143a2 --- /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..3fc19fde2c8f --- /dev/null +++ b/crates/.rustc_info.json @@ -0,0 +1 @@ +{"rustc_fingerprint":15139350435746051568,"outputs":{"7023940935747009273":{"success":true,"status":"","code":0,"stdout":"rustc 1.80.0-nightly (8c127df75 2024-05-16)\nbinary: rustc\ncommit-hash: 8c127df75fde3d5ad8ef9af664962a7676288b52\ncommit-date: 2024-05-16\nhost: x86_64-unknown-linux-gnu\nrelease: 1.80.0-nightly\nLLVM version: 18.1.4\n","stderr":""},"11653207418815599125":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n","stderr":""},"15054111900192202948":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/runner/.rustup/toolchains/nightly-2024-05-17-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\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_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..525ba947ecb1 --- /dev/null +++ b/crates/.rustdoc_fingerprint.json @@ -0,0 +1 @@ +{"rustc_vv":"rustc 1.80.0-nightly (8c127df75 2024-05-16)\nbinary: rustc\ncommit-hash: 8c127df75fde3d5ad8ef9af664962a7676288b52\ncommit-date: 2024-05-16\nhost: x86_64-unknown-linux-gnu\nrelease: 1.80.0-nightly\nLLVM version: 18.1.4\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..5e592b33aa22 --- /dev/null +++ b/crates/doc/crates.js @@ -0,0 +1 @@ +window.ALL_CRATES = ["kani"]; \ No newline at end of file diff --git a/crates/doc/help.html b/crates/doc/help.html new file mode 100644 index 000000000000..996b4f1e1049 --- /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..0407aae8fe33 --- /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..f2badc269c35 --- /dev/null +++ b/crates/doc/kani/arbitrary/index.html @@ -0,0 +1,4 @@ +kani::arbitrary - Rust

Module kani::arbitrary

source ·
Expand description

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

+

Traits§

  • This trait should be used to generate symbolic variables that represent any valid value of +its type.
\ 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..7718ccf98d8a --- /dev/null +++ b/crates/doc/kani/arbitrary/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"trait":["Arbitrary"]}; \ No newline at end of file diff --git a/crates/doc/kani/arbitrary/trait.Arbitrary.html b/crates/doc/kani/arbitrary/trait.Arbitrary.html new file mode 100644 index 000000000000..584b5359f71c --- /dev/null +++ b/crates/doc/kani/arbitrary/trait.Arbitrary.html @@ -0,0 +1,35 @@ +Arbitrary in kani::arbitrary - Rust

Trait kani::arbitrary::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] + where [(); { _ }]: { ... } +}
Expand description

This trait should be used to generate symbolic variables that represent any valid value of +its type.

+

Required Methods§

source

fn any() -> Self

Provided Methods§

source

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

Object Safety§

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 f32

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for f64

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for i8

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for i16

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for i32

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for i64

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for i128

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for isize

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for u8

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for u16

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for u32

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for u64

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for u128

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for ()

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

source§

impl Arbitrary for usize

source§

fn any() -> Self

source§

fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
where + [(); { _ }]:,

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 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, 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/attr.ensures.html b/crates/doc/kani/attr.ensures.html new file mode 100644 index 000000000000..77bb5506bd78 --- /dev/null +++ b/crates/doc/kani/attr.ensures.html @@ -0,0 +1,13 @@ +ensures in kani - Rust

Attribute Macro kani::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 condition over the input values to the +annotated function and its return value, accessible as a variable called +result. 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.modifies.html b/crates/doc/kani/attr.modifies.html new file mode 100644 index 000000000000..1de3c4e6eb50 --- /dev/null +++ b/crates/doc/kani/attr.modifies.html @@ -0,0 +1,13 @@ +modifies in kani - Rust

Attribute Macro kani::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..b26a484ba50b --- /dev/null +++ b/crates/doc/kani/attr.proof.html @@ -0,0 +1,6 @@ +proof in kani - Rust

Attribute Macro kani::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..bafe33d48302 --- /dev/null +++ b/crates/doc/kani/attr.proof_for_contract.html @@ -0,0 +1,7 @@ +proof_for_contract in kani - Rust

Attribute Macro kani::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..91797dd3bd5a --- /dev/null +++ b/crates/doc/kani/attr.recursion.html @@ -0,0 +1,5 @@ +recursion in kani - Rust

Attribute Macro kani::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..7c47d178f1f4 --- /dev/null +++ b/crates/doc/kani/attr.requires.html @@ -0,0 +1,12 @@ +requires in kani - Rust

Attribute Macro kani::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..5207280ec780 --- /dev/null +++ b/crates/doc/kani/attr.should_panic.html @@ -0,0 +1,10 @@ +should_panic in kani - Rust

Attribute Macro kani::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..d856b9779f9a --- /dev/null +++ b/crates/doc/kani/attr.solver.html @@ -0,0 +1,4 @@ +solver in kani - Rust

Attribute Macro kani::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..eab1c8cdf0da --- /dev/null +++ b/crates/doc/kani/attr.stub.html @@ -0,0 +1,8 @@ +stub in kani - Rust

Attribute Macro kani::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..fa5ee9a198cc --- /dev/null +++ b/crates/doc/kani/attr.stub_verified.html @@ -0,0 +1,11 @@ +stub_verified in kani - Rust

Attribute Macro kani::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..cebaf8c51039 --- /dev/null +++ b/crates/doc/kani/attr.unwind.html @@ -0,0 +1,4 @@ +unwind in kani - Rust

Attribute Macro kani::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..6a7096b4f1e9 --- /dev/null +++ b/crates/doc/kani/contracts/attr.ensures.html @@ -0,0 +1,13 @@ +ensures in kani::contracts - Rust

Attribute Macro kani::contracts::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 condition over the input values to the +annotated function and its return value, accessible as a variable called +result. 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..74f40aeecc35 --- /dev/null +++ b/crates/doc/kani/contracts/attr.modifies.html @@ -0,0 +1,13 @@ +modifies in kani::contracts - Rust

Attribute Macro kani::contracts::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..77bf653ca0d5 --- /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 kani::contracts::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..8f5e72c1cbd0 --- /dev/null +++ b/crates/doc/kani/contracts/attr.requires.html @@ -0,0 +1,12 @@ +requires in kani::contracts - Rust

Attribute Macro kani::contracts::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..2dfbbb44b3a9 --- /dev/null +++ b/crates/doc/kani/contracts/attr.stub_verified.html @@ -0,0 +1,11 @@ +stub_verified in kani::contracts - Rust

Attribute Macro kani::contracts::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..be6c4e7d15b5 --- /dev/null +++ b/crates/doc/kani/contracts/index.html @@ -0,0 +1,181 @@ +kani::contracts - Rust

Module kani::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: u32, divisor: u32) -> u32 {
+  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 <= 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 arguments the postcondition +also has access to the value returned from the function in a variable called +result.

+

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:

+ +
#[kani::requires(divisor != 0)]
+#[kani::ensures(result <= dividend)]
+fn my_div(dividend: u32, divisor: u32) -> u32 {
+  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 = vec![...];
+  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.

+

Attribute Macros§

  • Add a postcondition to this function.
  • Declaration of an explicit write-set for the annotated function.
  • Designates this function as a harness to check a function contract.
  • Add a precondition to this function.
  • 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..934fb3e6e521 --- /dev/null +++ b/crates/doc/kani/derive.Arbitrary.html @@ -0,0 +1,2 @@ +Arbitrary in kani - Rust

Derive Macro kani::Arbitrary

#[derive(Arbitrary)]
Expand description

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

+
\ 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..da2658312438 --- /dev/null +++ b/crates/doc/kani/fn.any.html @@ -0,0 +1,12 @@ +any in kani - Rust

Function kani::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::<std::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..90ffe8f485a5 --- /dev/null +++ b/crates/doc/kani/fn.any_where.html @@ -0,0 +1,13 @@ +any_where in kani - Rust

Function kani::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..a0694f669481 --- /dev/null +++ b/crates/doc/kani/fn.assert.html @@ -0,0 +1,6 @@ +assert in kani - Rust

Function kani::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..aa96934a1335 --- /dev/null +++ b/crates/doc/kani/fn.assume.html @@ -0,0 +1,17 @@ +assume in kani - Rust

Function kani::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..2f75ded34bec --- /dev/null +++ b/crates/doc/kani/fn.concrete_playback_run.html @@ -0,0 +1,2 @@ +concrete_playback_run in kani - Rust

Function kani::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..f164f5a98e68 --- /dev/null +++ b/crates/doc/kani/fn.cover.html @@ -0,0 +1,14 @@ +cover in kani - Rust

Function kani::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/futures/enum.SchedulingAssumption.html b/crates/doc/kani/futures/enum.SchedulingAssumption.html new file mode 100644 index 000000000000..028ad857f53f --- /dev/null +++ b/crates/doc/kani/futures/enum.SchedulingAssumption.html @@ -0,0 +1,17 @@ +SchedulingAssumption in kani::futures - Rust

Enum kani::futures::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>,

§

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>,

§

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..d2933a8a0856 --- /dev/null +++ b/crates/doc/kani/futures/fn.block_on.html @@ -0,0 +1,7 @@ +block_on in kani::futures - Rust

Function kani::futures::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..4e0b5e06a0b1 --- /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 kani::futures::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..d2586d4eb71e --- /dev/null +++ b/crates/doc/kani/futures/fn.spawn.html @@ -0,0 +1,3 @@ +spawn in kani::futures - Rust

Function kani::futures::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..fb3842e55190 --- /dev/null +++ b/crates/doc/kani/futures/fn.yield_now.html @@ -0,0 +1,3 @@ +yield_now in kani::futures - Rust

Function kani::futures::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..14d53c0e32ee --- /dev/null +++ b/crates/doc/kani/futures/index.html @@ -0,0 +1,2 @@ +kani::futures - Rust

Module kani::futures

source ·
Expand description

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

+

Structs§

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

Enums§

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

Traits§

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

Functions§

  • A very simple executor: it polls the future in a busy loop until completion
  • Polls the given future and the tasks it may spawn until all of them complete
  • Spawns a task on the current global executor (which is set by block_on_with_spawn)
  • 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..cfa7f33830af --- /dev/null +++ b/crates/doc/kani/futures/struct.JoinHandle.html @@ -0,0 +1,15 @@ +JoinHandle in kani::futures - Rust

Struct kani::futures::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

§

type Output = ()

The type of value produced on completion.
source§

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

Attempt 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,

§

type Output = <F as Future>::Output

The output that the future will produce on completion.
§

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>,

§

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>,

§

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..f47a40b4588f --- /dev/null +++ b/crates/doc/kani/futures/struct.RoundRobin.html @@ -0,0 +1,12 @@ +RoundRobin in kani::futures - Rust

Struct kani::futures::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>,

§

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>,

§

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..eea57ce6616e --- /dev/null +++ b/crates/doc/kani/futures/trait.SchedulingStrategy.html @@ -0,0 +1,25 @@ +SchedulingStrategy in kani::futures - Rust

Trait kani::futures::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..648236d0c31e --- /dev/null +++ b/crates/doc/kani/index.html @@ -0,0 +1,16 @@ +kani - Rust

Crate kani

source ·

Re-exports§

Modules§

  • This module introduces the Arbitrary trait as well as implementation for primitive types and +other std containers.
  • Kani implementation of function contracts.
  • This module contains functions to work with futures (and async/.await) in Kani.
  • This module contains functions useful for checking unsafe memory access.
  • Support for arbitrary tuples where each element implements +kani::Arbitrary. Tuples of size up to 12 are supported in this +file.

Macros§

  • A macro to check if a condition is satisfiable at a specific location in the +code.
  • implies!(premise => conclusion) means that if the premise is true, so +must be the conclusion.

Functions§

  • 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.
  • 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.
  • Creates an assertion of the specified condition and message.
  • 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.
  • NOP concrete_playback for type checking during verification mode.
  • Creates a cover property with the specified condition and message.

Attribute Macros§

  • Add a postcondition to this function.
  • Declaration of an explicit write-set for the annotated function.
  • Marks a Kani proof harness
  • Designates this function as a harness to check a function contract.
  • Specifies that a function contains recursion for contract instrumentation.**
  • Add a precondition to this function.
  • Specifies that a proof harness is expected to panic.**
  • Select the SAT solver to use with CBMC for this harness
  • Specify a function/method stub pair to use for proof harness
  • 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.
  • 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§

  • Allow users to auto generate Arbitrary implementations by using #[derive(Arbitrary)] macro.
\ 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..c92e39249f1a --- /dev/null +++ b/crates/doc/kani/macro.cover.html @@ -0,0 +1,28 @@ +cover in kani - Rust

Macro kani::cover

source ·
macro_rules! cover {
+    () => { ... };
+    ($cond:expr $(,)?) => { ... };
+    ($cond:expr, $msg:literal) => { ... };
+}
Expand description

A macro to check if a condition is satisfiable at a specific location in the +code.

+

§Example 1:

+
let mut set: BTreeSet<i32> = BTreeSet::new();
+set.insert(kani::any());
+set.insert(kani::any());
+// check if the set can end up with a single element (if both elements
+// inserted were the same)
+kani::cover!(set.len() == 1);
+

The macro can also be called without any arguments to check if a location is +reachable.

+

§Example 2:

+
match e {
+    MyEnum::A => { /* .. */ }
+    MyEnum::B => {
+        // make sure the `MyEnum::B` variant is possible
+        kani::cover!();
+        // ..
+    }
+}
+

A custom message can also be passed to the macro.

+

§Example 3:

+
kani::cover!(x > y, "x can be greater than y")
+
\ 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..807a26bffaf5 --- /dev/null +++ b/crates/doc/kani/macro.implies.html @@ -0,0 +1,7 @@ +implies in kani - Rust

Macro kani::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/mem/fn.assert_valid_ptr.html b/crates/doc/kani/mem/fn.assert_valid_ptr.html new file mode 100644 index 000000000000..1764eb70b0d4 --- /dev/null +++ b/crates/doc/kani/mem/fn.assert_valid_ptr.html @@ -0,0 +1,9 @@ +assert_valid_ptr in kani::mem - Rust

Function kani::mem::assert_valid_ptr

source ·
pub fn assert_valid_ptr<T>(ptr: *const T) -> bool
where + T: ?Sized, + <T as Pointee>::Metadata: PtrProperties<T>,
Expand description

Assert that the pointer is valid for access according to crate::mem conditions 1, 2 and 3.

+

Note that an unaligned pointer is still considered valid.

+

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

+

This function will either panic or return true. This is to make it easier to use it in +contracts.

+
\ 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..69d481f5fd31 --- /dev/null +++ b/crates/doc/kani/mem/index.html @@ -0,0 +1,32 @@ +kani::mem - Rust

Module kani::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.
  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§

\ 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..67f1631dd5be --- /dev/null +++ b/crates/doc/kani/mem/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["assert_valid_ptr"]}; \ 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..14126e23fe03 --- /dev/null +++ b/crates/doc/kani/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"attr":["ensures","modifies","proof","proof_for_contract","recursion","requires","should_panic","solver","stub","stub_verified","unwind"],"derive":["Arbitrary"],"fn":["any","any_where","assert","assume","concrete_playback_run","cover"],"macro":["cover","implies"],"mod":["arbitrary","contracts","futures","mem","slice","tuple","vec"]}; \ 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..fd9ae74e3220 --- /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 kani::slice::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..83fdb898f7b4 --- /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 kani::slice::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..9b7bc412d1b1 --- /dev/null +++ b/crates/doc/kani/slice/index.html @@ -0,0 +1,4 @@ +kani::slice - Rust

Module kani::slice

source ·

Functions§

  • 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.
  • 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/tuple/index.html b/crates/doc/kani/tuple/index.html new file mode 100644 index 000000000000..da54fb62e09a --- /dev/null +++ b/crates/doc/kani/tuple/index.html @@ -0,0 +1,4 @@ +kani::tuple - Rust

Module kani::tuple

source ·
Expand description

Support for arbitrary tuples where each element implements +kani::Arbitrary. Tuples of size up to 12 are supported in this +file.

+
\ No newline at end of file diff --git a/crates/doc/kani/tuple/sidebar-items.js b/crates/doc/kani/tuple/sidebar-items.js new file mode 100644 index 000000000000..5244ce01ccff --- /dev/null +++ b/crates/doc/kani/tuple/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {}; \ 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..d4f310d165d3 --- /dev/null +++ b/crates/doc/kani/vec/fn.any_vec.html @@ -0,0 +1,4 @@ +any_vec in kani::vec - Rust

Function kani::vec::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..b44c52fb262d --- /dev/null +++ b/crates/doc/kani/vec/fn.exact_vec.html @@ -0,0 +1,4 @@ +exact_vec in kani::vec - Rust

Function kani::vec::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..2bb3a646ada7 --- /dev/null +++ b/crates/doc/kani/vec/index.html @@ -0,0 +1 @@ +kani::vec - Rust

Module kani::vec

source ·

Functions§

  • Generates an arbitrary vector whose length is at most MAX_LENGTH.
  • 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..7464c174ae9d --- /dev/null +++ b/crates/doc/search-index.js @@ -0,0 +1,5 @@ +var searchIndex = new Map(JSON.parse('[\ +["kani",{"t":"EYEHHCHHEEHCHQXCQCXXXXXXCXEXXCXCEKMNXXXXXPPFFGKHHNNNNNNNNNNNNNNMNNHNNNNNNNNNHHHHHH","n":["Arbitrary","Arbitrary","RoundRobin","any","any_where","arbitrary","assert","assume","block_on","block_on_with_spawn","concrete_playback_run","contracts","cover","cover","ensures","futures","implies","mem","modifies","proof","proof_for_contract","recursion","requires","should_panic","slice","solver","spawn","stub","stub_verified","tuple","unwind","vec","yield_now","Arbitrary","any","any_array","ensures","modifies","proof_for_contract","requires","stub_verified","CanAssumeRunning","CannotAssumeRunning","JoinHandle","RoundRobin","SchedulingAssumption","SchedulingStrategy","block_on","block_on_with_spawn","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","default","from","from","from","into","into","into","into_future","pick_task","pick_task","poll","spawn","try_from","try_from","try_from","try_into","try_into","try_into","type_id","type_id","type_id","yield_now","assert_valid_ptr","any_slice_of_array","any_slice_of_array_mut","any_vec","exact_vec"],"q":[[0,"kani"],[33,"kani::arbitrary"],[36,"kani::contracts"],[41,"kani::futures"],[77,"kani::mem"],[78,"kani::slice"],[80,"kani::vec"],[82,"core::ops::function"],[83,"alloc::vec"],[84,"core::future::future"],[85,"core::marker"],[86,"core::pin"],[87,"core::task::wake"],[88,"core::task::poll"],[89,"core::result"],[90,"core::any"],[91,"kani_macros"]],"i":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,18,18,0,0,0,0,0,0,18,20,16,18,20,16,16,18,20,16,18,20,16,20,14,16,20,0,18,20,16,18,20,16,18,20,16,0,0,0,0,0,0],"f":"```{{}cb}{ecb{{j{{d{c}}}{{f{h}}}}}}`{{h{d{l}}}n}{hn}``{{{Ab{{Ab{A`}}}}c}nAd}`2`````````````````````{{}b}{{}{{Af{b}}}}```````````{ec{}{{Ah{}{{f{c}}}}}}{{ce}n{{Ah{}{{f{n}}}}Aj}Al}{{{d{c}}}{{d{e}}}{}{}}00{{{d{Anc}}}{{d{Ane}}}{}{}}00{{}B`}{cc{}}00{ce{}{}}00{c{}{}}{{{d{AnAl}}Bb}{{Bf{BbBd}}}}{{{d{AnB`}}Bb}{{Bf{BbBd}}}}{{{Bj{{d{AnBh}}}}{d{AnBl}}}{{Bn{c}}}{}}{cBh{{Ah{}{{f{n}}}}Aj}}{c{{C`{e}}}{}{}}00000{{{d{c}}}Cb{}}00{{}{{`{{Ah{}{{f{n}}}}}}}}{{}h}{{{d{{Af{c}}}}}{{d{{Cd{c}}}}}{}}{{{d{An{Af{c}}}}}{{d{An{Cd{c}}}}}{}}{{}{{Ab{c}}}b}0","D":"Ff","p":[[10,"Arbitrary",33],[1,"reference"],[17,"Output"],[1,"bool"],[10,"FnOnce",82],[1,"str"],[1,"unit"],[1,"u8"],[5,"Vec",83],[10,"Fn",82],[1,"array"],[10,"Future",84],[10,"Sync",85],[10,"SchedulingStrategy",41],[0,"mut"],[5,"RoundRobin",41],[1,"usize"],[6,"SchedulingAssumption",41],[1,"tuple"],[5,"JoinHandle",41],[5,"Pin",86],[5,"Context",87],[6,"Poll",88],[6,"Result",89],[5,"TypeId",90],[1,"slice"]],"r":[[0,33],[1,91],[2,41],[8,41],[9,41],[14,91],[18,91],[19,91],[20,91],[21,91],[22,91],[23,91],[25,91],[26,41],[27,91],[28,91],[30,91],[32,41],[36,91],[37,91],[38,91],[39,91],[40,91]],"b":[],"c":"OjAAAAAAAAA=","e":"OzAAAAEAAB8ADAAAAAEAAwAAAAkAAQAZAAAAGwAAACAAAQAjAAEAKgABADIABgA/AAAAQQABAEQACAA="}]\ +]')); +if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; +else if (window.initSearch) window.initSearch(searchIndex); 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..7f1aa6ec25d5 --- /dev/null +++ b/crates/doc/search.desc/kani/kani-desc-0-.js @@ -0,0 +1 @@ +searchState.loadedDescShard("kani", 0, "Allow users to auto generate Arbitrary implementations by …\nThis creates an symbolic valid value of type T. You can …\nThis creates a symbolic valid value of type T. The value …\nThis module introduces the Arbitrary trait as well as …\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 …\nA macro to check if a condition is satisfiable at a …\nAdd a postcondition to this function.\nThis module contains functions to work with futures (and …\nimplies!(premise => conclusion) means that if the premise …\nThis module contains functions useful for checking unsafe …\nDeclaration of an explicit write-set for the annotated …\nMarks a Kani proof harness\nDesignates this function as a harness to check a function …\nSpecifies that a function contains recursion for contract …\nAdd a precondition to this function.\nSpecifies that a proof harness is expected to panic.**\nSelect the SAT solver to use with CBMC for this harness\nSpecify a function/method stub pair to use for proof …\nstub_verified(TARGET) is a harness attribute (to be used on\nSupport for arbitrary tuples where each element implements …\nSet Loop unwind limit for proof harnesses The attribute …\nThis trait should be used to generate symbolic variables …\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\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 …\nAssert that the pointer is valid for access according to …\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..1677495c7783 --- /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..2149346a531b --- /dev/null +++ b/crates/doc/src-files.js @@ -0,0 +1,4 @@ +var srcIndex = new Map(JSON.parse('[\ +["kani",["",[["models",[],["mod.rs"]]],["arbitrary.rs","contracts.rs","futures.rs","internal.rs","lib.rs","mem.rs","slice.rs","tuple.rs","vec.rs"]]]\ +]')); +createSrcSidebar(); diff --git a/crates/doc/src/kani/arbitrary.rs.html b/crates/doc/src/kani/arbitrary.rs.html new file mode 100644 index 000000000000..1d9afe9db1d3 --- /dev/null +++ b/crates/doc/src/kani/arbitrary.rs.html @@ -0,0 +1,363 @@ +arbitrary.rs - source
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
+
// 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 std::{
+    marker::{PhantomData, PhantomPinned},
+    num::*,
+};
+
+/// This trait should be used to generate symbolic variables that represent any valid value of
+/// its type.
+pub trait Arbitrary
+where
+    Self: Sized,
+{
+    fn any() -> Self;
+    fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
+    // the requirement defined in the where clause must appear on the `impl`'s method `any_array`
+    // but also on the corresponding trait's method
+    where
+        [(); std::mem::size_of::<[Self; MAX_ARRAY_LENGTH]>()]:,
+    {
+        [(); MAX_ARRAY_LENGTH].map(|_| Self::any())
+    }
+}
+
+/// The given type can be represented by an unconstrained symbolic value of size_of::<T>.
+macro_rules! trivial_arbitrary {
+    ( $type: ty ) => {
+        impl Arbitrary for $type {
+            #[inline(always)]
+            fn any() -> Self {
+                // This size_of call does not use generic_const_exprs feature. It's inside a macro, and Self isn't generic.
+                unsafe { crate::any_raw_internal::<Self, { std::mem::size_of::<Self>() }>() }
+            }
+            fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH]
+            where
+                // `generic_const_exprs` requires all potential errors to be reflected in the signature/header.
+                // We must repeat the expression in the header, to make sure that if the body can fail the header will also fail.
+                [(); { std::mem::size_of::<[$type; MAX_ARRAY_LENGTH]>() }]:,
+            {
+                unsafe {
+                    crate::any_raw_internal::<
+                        [Self; MAX_ARRAY_LENGTH],
+                        { std::mem::size_of::<[Self; MAX_ARRAY_LENGTH]>() },
+                    >()
+                }
+            }
+        }
+    };
+}
+
+trivial_arbitrary!(u8);
+trivial_arbitrary!(u16);
+trivial_arbitrary!(u32);
+trivial_arbitrary!(u64);
+trivial_arbitrary!(u128);
+trivial_arbitrary!(usize);
+
+trivial_arbitrary!(i8);
+trivial_arbitrary!(i16);
+trivial_arbitrary!(i32);
+trivial_arbitrary!(i64);
+trivial_arbitrary!(i128);
+trivial_arbitrary!(isize);
+
+// We do not constraint floating points values per type spec. Users must add assumptions to their
+// verification code if they want to eliminate NaN, infinite, or subnormal.
+trivial_arbitrary!(f32);
+trivial_arbitrary!(f64);
+
+trivial_arbitrary!(());
+
+impl Arbitrary for bool {
+    #[inline(always)]
+    fn any() -> Self {
+        let byte = u8::any();
+        crate::assume(byte < 2);
+        byte == 1
+    }
+}
+
+/// 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>
+impl Arbitrary for char {
+    #[inline(always)]
+    fn any() -> Self {
+        // Generate an arbitrary u32 and constrain it to make it a valid representation of char.
+        let val = u32::any();
+        crate::assume(val <= 0xD7FF || (0xE000..=0x10FFFF).contains(&val));
+        unsafe { char::from_u32_unchecked(val) }
+    }
+}
+
+macro_rules! nonzero_arbitrary {
+    ( $type: ty, $base: ty ) => {
+        impl Arbitrary for $type {
+            #[inline(always)]
+            fn any() -> Self {
+                let val = <$base>::any();
+                crate::assume(val != 0);
+                unsafe { <$type>::new_unchecked(val) }
+            }
+        }
+    };
+}
+
+nonzero_arbitrary!(NonZeroU8, u8);
+nonzero_arbitrary!(NonZeroU16, u16);
+nonzero_arbitrary!(NonZeroU32, u32);
+nonzero_arbitrary!(NonZeroU64, u64);
+nonzero_arbitrary!(NonZeroU128, u128);
+nonzero_arbitrary!(NonZeroUsize, usize);
+
+nonzero_arbitrary!(NonZeroI8, i8);
+nonzero_arbitrary!(NonZeroI16, i16);
+nonzero_arbitrary!(NonZeroI32, i32);
+nonzero_arbitrary!(NonZeroI64, i64);
+nonzero_arbitrary!(NonZeroI128, i128);
+nonzero_arbitrary!(NonZeroIsize, isize);
+
+impl<T, const N: usize> Arbitrary for [T; N]
+where
+    T: Arbitrary,
+    [(); std::mem::size_of::<[T; N]>()]:,
+{
+    fn any() -> Self {
+        T::any_array()
+    }
+}
+
+impl<T> Arbitrary for Option<T>
+where
+    T: Arbitrary,
+{
+    fn any() -> Self {
+        if bool::any() { Some(T::any()) } else { None }
+    }
+}
+
+impl<T, E> Arbitrary for Result<T, E>
+where
+    T: Arbitrary,
+    E: Arbitrary,
+{
+    fn any() -> Self {
+        if bool::any() { Ok(T::any()) } else { Err(E::any()) }
+    }
+}
+
+impl<T: ?Sized> Arbitrary for std::marker::PhantomData<T> {
+    fn any() -> Self {
+        PhantomData
+    }
+}
+
+impl Arbitrary for std::marker::PhantomPinned {
+    fn any() -> Self {
+        PhantomPinned
+    }
+}
+
+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..a420522578a3 --- /dev/null +++ b/crates/doc/src/kani/contracts.rs.html @@ -0,0 +1,457 @@ +contracts.rs - source
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
+
// 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: u32, divisor: u32) -> u32 {
+//!   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`.
+//!
+//! ```ignore
+//! #[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`][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:
+//!
+//! ```
+//! #[ensures(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`][macro@requires]. In addition to the arguments the postcondition
+//! also has access to the value returned from the function in a variable called
+//! `result`.
+//!
+//! 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:
+//!
+//! ```
+//! #[kani::requires(divisor != 0)]
+//! #[kani::ensures(result <= dividend)]
+//! fn my_div(dividend: u32, divisor: u32) -> u32 {
+//!   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.
+//!
+//! ```
+//! #[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.
+//!
+//! ```
+//! #[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];
+//! }
+//! ```
+//!
+//! 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`.
+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..e38e53143f01 --- /dev/null +++ b/crates/doc/src/kani/futures.rs.html @@ -0,0 +1,469 @@ +futures.rs - source
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
+
// 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,
+}
+
+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")]
+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")]
+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/internal.rs.html b/crates/doc/src/kani/internal.rs.html new file mode 100644 index 000000000000..97feb3f48fda --- /dev/null +++ b/crates/doc/src/kani/internal.rs.html @@ -0,0 +1,157 @@ +internal.rs - source
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
+
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+/// Helper trait for code generation for `modifies` contracts.
+///
+/// We allow the user to provide us with a pointer-like object that we convert as needed.
+#[doc(hidden)]
+pub trait Pointer<'a> {
+    /// Type of the pointed-to data
+    type Inner;
+
+    /// Used for checking assigns contracts where we pass immutable references to the function.
+    ///
+    /// We're using a reference to self here, because the user can use just a plain function
+    /// argument, for instance one of type `&mut _`, in the `modifies` clause which would move it.
+    unsafe fn decouple_lifetime(&self) -> &'a Self::Inner;
+
+    /// used for havocking on replecement of a `modifies` clause.
+    unsafe fn assignable(self) -> &'a mut Self::Inner;
+}
+
+impl<'a, 'b, T> Pointer<'a> for &'b T {
+    type Inner = T;
+    unsafe fn decouple_lifetime(&self) -> &'a Self::Inner {
+        std::mem::transmute(*self)
+    }
+
+    #[allow(clippy::transmute_ptr_to_ref)]
+    unsafe fn assignable(self) -> &'a mut Self::Inner {
+        std::mem::transmute(self as *const T)
+    }
+}
+
+impl<'a, 'b, T> Pointer<'a> for &'b mut T {
+    type Inner = T;
+
+    #[allow(clippy::transmute_ptr_to_ref)]
+    unsafe fn decouple_lifetime(&self) -> &'a Self::Inner {
+        std::mem::transmute::<_, &&'a T>(self)
+    }
+
+    unsafe fn assignable(self) -> &'a mut Self::Inner {
+        std::mem::transmute(self)
+    }
+}
+
+impl<'a, T> Pointer<'a> for *const T {
+    type Inner = T;
+    unsafe fn decouple_lifetime(&self) -> &'a Self::Inner {
+        &**self as &'a T
+    }
+
+    #[allow(clippy::transmute_ptr_to_ref)]
+    unsafe fn assignable(self) -> &'a mut Self::Inner {
+        std::mem::transmute(self)
+    }
+}
+
+impl<'a, T> Pointer<'a> for *mut T {
+    type Inner = T;
+    unsafe fn decouple_lifetime(&self) -> &'a Self::Inner {
+        &**self as &'a T
+    }
+
+    #[allow(clippy::transmute_ptr_to_ref)]
+    unsafe fn assignable(self) -> &'a mut Self::Inner {
+        std::mem::transmute(self)
+    }
+}
+
+/// A way to break the ownerhip rules. Only used by contracts where we can
+/// guarantee it is done safely.
+#[inline(never)]
+#[doc(hidden)]
+#[rustc_diagnostic_item = "KaniUntrackedDeref"]
+pub fn untracked_deref<T>(_: &T) -> T {
+    todo!()
+}
+
\ 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..382776a66e4f --- /dev/null +++ b/crates/doc/src/kani/lib.rs.html @@ -0,0 +1,647 @@ +lib.rs - source
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
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+
// 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)]
+// This is required for the optimized version of `any_array()`
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+// Used to model simd.
+#![feature(repr_simd)]
+// 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(ptr_metadata)]
+
+pub mod arbitrary;
+#[cfg(feature = "concrete_playback")]
+mod concrete_playback;
+pub mod futures;
+pub mod mem;
+pub mod slice;
+pub mod tuple;
+pub mod vec;
+
+#[doc(hidden)]
+pub mod internal;
+
+mod models;
+
+pub use arbitrary::Arbitrary;
+#[cfg(feature = "concrete_playback")]
+pub use concrete_playback::concrete_playback_run;
+
+#[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::{block_on, block_on_with_spawn, spawn, yield_now, RoundRobin};
+
+/// 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.
+///
+/// ```rust
+/// let i : i32 = kani::any();
+/// kani::assume(i > 10);
+/// if i < 0 {
+///   panic!("This will never panic");
+/// }
+/// ```
+///
+/// The following code may panic though:
+///
+/// ```rust
+/// let i : i32 = kani::any();
+/// assert!(i < 0, "This may panic and verification should fail.");
+/// kani::assume(i > 10);
+/// ```
+#[inline(never)]
+#[rustc_diagnostic_item = "KaniAssume"]
+#[cfg(not(feature = "concrete_playback"))]
+pub fn assume(cond: bool) {
+    let _ = cond;
+}
+
+#[inline(never)]
+#[rustc_diagnostic_item = "KaniAssume"]
+#[cfg(feature = "concrete_playback")]
+pub fn assume(cond: bool) {
+    assert!(cond, "`kani::assume` should always hold");
+}
+
+/// `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)
+    };
+}
+
+/// Creates an assertion of the specified condition and message.
+///
+/// # Example:
+///
+/// ```rust
+/// let x: bool = kani::any();
+/// let y = !x;
+/// kani::assert(x || y, "ORing a boolean variable with its negation must be true")
+/// ```
+#[cfg(not(feature = "concrete_playback"))]
+#[inline(never)]
+#[rustc_diagnostic_item = "KaniAssert"]
+pub const fn assert(cond: bool, msg: &'static str) {
+    let _ = cond;
+    let _ = msg;
+}
+
+#[cfg(feature = "concrete_playback")]
+#[inline(never)]
+#[rustc_diagnostic_item = "KaniAssert"]
+pub const fn assert(cond: bool, msg: &'static str) {
+    assert!(cond, "{}", msg);
+}
+
+/// Creates a cover property with the specified condition and message.
+///
+/// # Example:
+///
+/// ```rust
+/// 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.
+///
+#[inline(never)]
+#[rustc_diagnostic_item = "KaniCover"]
+pub const fn cover(_cond: bool, _msg: &'static str) {}
+
+/// 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.
+///
+/// ```rust
+/// let inputA = kani::any::<std::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`.
+#[rustc_diagnostic_item = "KaniAny"]
+#[inline(always)]
+pub fn any<T: Arbitrary>() -> T {
+    T::any()
+}
+
+/// This function is only used for function contract instrumentation.
+/// It behaves exaclty like `kani::any<T>()`, except it will check for the trait bounds
+/// at compilation time. It allows us to avoid type checking errors while using function
+/// contracts only for verification.
+#[rustc_diagnostic_item = "KaniAnyModifies"]
+#[inline(never)]
+#[doc(hidden)]
+pub fn any_modifies<T>() -> T {
+    // This function should not be reacheable.
+    // Users must include `#[kani::recursion]` in any function contracts for recursive functions;
+    // otherwise, this might not be properly instantiate. We mark this as unreachable to make
+    // sure Kani doesn't report any false positives.
+    unreachable!()
+}
+
+/// 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.
+///
+/// ```rust
+/// 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`.
+#[inline(always)]
+pub fn any_where<T: Arbitrary, F: FnOnce(&T) -> bool>(f: F) -> T {
+    let result = T::any();
+    assume(f(&result));
+    result
+}
+
+/// This function creates a symbolic value of type `T`. This may result in an invalid value.
+///
+/// # Safety
+///
+/// This function is unsafe and it may represent invalid `T` values which can lead to many
+/// undesirable undefined behaviors. Because of that, this function can only be used
+/// internally when we can guarantee that the type T has no restriction regarding its bit level
+/// representation.
+///
+/// This function is also used to find concrete values in the CBMC output trace
+/// and return those concrete values in concrete playback mode.
+///
+/// Note that SIZE_T must be equal the size of type T in bytes.
+#[inline(never)]
+#[cfg(not(feature = "concrete_playback"))]
+pub(crate) unsafe fn any_raw_internal<T, const SIZE_T: usize>() -> T {
+    any_raw_inner::<T>()
+}
+
+#[inline(never)]
+#[cfg(feature = "concrete_playback")]
+pub(crate) unsafe fn any_raw_internal<T, const SIZE_T: usize>() -> T {
+    concrete_playback::any_raw_internal::<T, SIZE_T>()
+}
+
+/// This low-level function returns nondet bytes of size T.
+#[rustc_diagnostic_item = "KaniAnyRaw"]
+#[inline(never)]
+#[allow(dead_code)]
+fn any_raw_inner<T>() -> T {
+    kani_intrinsic()
+}
+
+/// Function used to generate panic with a static message as this is the only one currently
+/// supported by Kani display.
+///
+/// During verification this will get replaced by `assert(false)`. For concrete executions, we just
+/// invoke the regular `std::panic!()` function. This function is used by our standard library
+/// overrides, but not the other way around.
+#[inline(never)]
+#[rustc_diagnostic_item = "KaniPanic"]
+#[doc(hidden)]
+pub const fn panic(message: &'static str) -> ! {
+    panic!("{}", message)
+}
+
+/// An empty body that can be used to define Kani intrinsic functions.
+///
+/// A Kani intrinsic is a function that is interpreted by Kani compiler.
+/// While we could use `unreachable!()` or `panic!()` as the body of a kani intrinsic
+/// function, both cause Kani to produce a warning since we don't support caller location.
+/// (see https://github.com/model-checking/kani/issues/2010).
+///
+/// This function is dead, since its caller is always  handled via a hook anyway,
+/// so we just need to put a body that rustc does not complain about.
+/// An infinite loop works out nicely.
+fn kani_intrinsic<T>() -> T {
+    #[allow(clippy::empty_loop)]
+    loop {}
+}
+/// A macro to check if a condition is satisfiable at a specific location in the
+/// code.
+///
+/// # Example 1:
+///
+/// ```rust
+/// let mut set: BTreeSet<i32> = BTreeSet::new();
+/// set.insert(kani::any());
+/// set.insert(kani::any());
+/// // check if the set can end up with a single element (if both elements
+/// // inserted were the same)
+/// kani::cover!(set.len() == 1);
+/// ```
+/// The macro can also be called without any arguments to check if a location is
+/// reachable.
+///
+/// # Example 2:
+///
+/// ```rust
+/// match e {
+///     MyEnum::A => { /* .. */ }
+///     MyEnum::B => {
+///         // make sure the `MyEnum::B` variant is possible
+///         kani::cover!();
+///         // ..
+///     }
+/// }
+/// ```
+///
+/// A custom message can also be passed to the macro.
+///
+/// # Example 3:
+///
+/// ```rust
+/// kani::cover!(x > y, "x can be greater than y")
+/// ```
+#[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);
+    };
+}
+
+// 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;
+
+// Kani proc macros must be in a separate crate
+pub use kani_macros::*;
+
+pub mod contracts;
+
\ No newline at end of file diff --git a/crates/doc/src/kani/mem.rs.html b/crates/doc/src/kani/mem.rs.html new file mode 100644 index 000000000000..06c5d28caaea --- /dev/null +++ b/crates/doc/src/kani/mem.rs.html @@ -0,0 +1,527 @@ +mem.rs - source
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
+
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+//! 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. 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.
+//! 3. 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`.
+//! 4. 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.
+//! 5. 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.
+//!
+//! 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.
+
+use crate::kani_intrinsic;
+use crate::mem::private::Internal;
+use std::mem::{align_of, size_of};
+use std::ptr::{DynMetadata, NonNull, Pointee};
+
+/// Assert that the pointer is valid for access according to [crate::mem] conditions 1, 2 and 3.
+///
+/// Note that an unaligned pointer is still considered valid.
+///
+/// TODO: Kani should automatically add those checks when a de-reference happens.
+/// <https://github.com/model-checking/kani/issues/2975>
+///
+/// This function will either panic or return `true`. This is to make it easier to use it in
+/// contracts.
+#[crate::unstable(
+    feature = "mem-predicates",
+    issue = 2690,
+    reason = "experimental memory predicate API"
+)]
+pub fn assert_valid_ptr<T>(ptr: *const T) -> bool
+where
+    T: ?Sized,
+    <T as Pointee>::Metadata: PtrProperties<T>,
+{
+    crate::assert(!ptr.is_null(), "Expected valid pointer, but found `null`");
+
+    let (thin_ptr, metadata) = ptr.to_raw_parts();
+    let sz = metadata.pointee_size(Internal);
+    if sz == 0 {
+        true // ZST pointers are always valid
+    } else {
+        // Note that this branch can't be tested in concrete execution as `is_read_ok` needs to be
+        // stubbed.
+        crate::assert(
+            is_read_ok(thin_ptr, sz),
+            "Expected valid pointer, but found dangling pointer",
+        );
+        true
+    }
+}
+
+mod private {
+    /// Define like this to restrict usage of PtrProperties functions outside Kani.
+    #[derive(Copy, Clone)]
+    pub struct Internal;
+}
+
+/// Trait that allow us to extract information from pointers without de-referencing them.
+#[doc(hidden)]
+pub trait PtrProperties<T: ?Sized> {
+    fn pointee_size(&self, _: Internal) -> usize;
+
+    fn min_alignment(&self, _: Internal) -> usize;
+
+    fn dangling(&self, _: Internal) -> *const ();
+}
+
+/// Get the information for sized types (they don't have metadata).
+impl<T> PtrProperties<T> for () {
+    fn pointee_size(&self, _: Internal) -> usize {
+        size_of::<T>()
+    }
+
+    fn min_alignment(&self, _: Internal) -> usize {
+        align_of::<T>()
+    }
+
+    fn dangling(&self, _: Internal) -> *const () {
+        NonNull::<T>::dangling().as_ptr() as *const _
+    }
+}
+
+/// Get the information from the str metadata.
+impl PtrProperties<str> for usize {
+    #[inline(always)]
+    fn pointee_size(&self, _: Internal) -> usize {
+        *self
+    }
+
+    /// String slices are a UTF-8 representation of characters that have the same layout as slices
+    /// of type [u8].
+    /// <https://doc.rust-lang.org/reference/type-layout.html#str-layout>
+    fn min_alignment(&self, _: Internal) -> usize {
+        align_of::<u8>()
+    }
+
+    fn dangling(&self, _: Internal) -> *const () {
+        NonNull::<u8>::dangling().as_ptr() as _
+    }
+}
+
+/// Get the information from the slice metadata.
+impl<T> PtrProperties<[T]> for usize {
+    fn pointee_size(&self, _: Internal) -> usize {
+        *self * size_of::<T>()
+    }
+
+    fn min_alignment(&self, _: Internal) -> usize {
+        align_of::<T>()
+    }
+
+    fn dangling(&self, _: Internal) -> *const () {
+        NonNull::<T>::dangling().as_ptr() as _
+    }
+}
+
+/// Get the information from the vtable.
+impl<T> PtrProperties<T> for DynMetadata<T>
+where
+    T: ?Sized,
+{
+    fn pointee_size(&self, _: Internal) -> usize {
+        self.size_of()
+    }
+
+    fn min_alignment(&self, _: Internal) -> usize {
+        self.align_of()
+    }
+
+    fn dangling(&self, _: Internal) -> *const () {
+        NonNull::<&T>::dangling().as_ptr() as _
+    }
+}
+
+/// Check if the pointer `_ptr` contains an allocated address of size equal or greater than `_size`.
+///
+/// This function should only be called to ensure a pointer is valid. The opposite isn't true.
+/// I.e.: This function always returns `true` if the pointer is valid.
+/// Otherwise, it returns non-det boolean.
+#[rustc_diagnostic_item = "KaniIsReadOk"]
+#[inline(never)]
+fn is_read_ok(_ptr: *const (), _size: usize) -> bool {
+    kani_intrinsic()
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{assert_valid_ptr, PtrProperties};
+    use crate::mem::private::Internal;
+    use std::fmt::Debug;
+    use std::intrinsics::size_of;
+    use std::mem::{align_of, align_of_val, size_of_val};
+    use std::ptr;
+    use std::ptr::{NonNull, Pointee};
+
+    fn size_of_t<T>(ptr: *const T) -> usize
+    where
+        T: ?Sized,
+        <T as Pointee>::Metadata: PtrProperties<T>,
+    {
+        let (_, metadata) = ptr.to_raw_parts();
+        metadata.pointee_size(Internal)
+    }
+
+    fn align_of_t<T>(ptr: *const T) -> usize
+    where
+        T: ?Sized,
+        <T as Pointee>::Metadata: PtrProperties<T>,
+    {
+        let (_, metadata) = ptr.to_raw_parts();
+        metadata.min_alignment(Internal)
+    }
+
+    #[test]
+    fn test_size_of() {
+        assert_eq!(size_of_t("hi"), size_of_val("hi"));
+        assert_eq!(size_of_t(&0u8), size_of_val(&0u8));
+        assert_eq!(size_of_t(&0u8 as *const dyn std::fmt::Display), size_of_val(&0u8));
+        assert_eq!(size_of_t(&[0u8, 1u8] as &[u8]), size_of_val(&[0u8, 1u8]));
+        assert_eq!(size_of_t(&[] as &[u8]), size_of_val::<[u8; 0]>(&[]));
+        assert_eq!(
+            size_of_t(NonNull::<u32>::dangling().as_ptr() as *const dyn std::fmt::Display),
+            size_of::<u32>()
+        );
+    }
+
+    #[test]
+    fn test_alignment() {
+        assert_eq!(align_of_t("hi"), align_of_val("hi"));
+        assert_eq!(align_of_t(&0u8), align_of_val(&0u8));
+        assert_eq!(align_of_t(&0u32 as *const dyn std::fmt::Display), align_of_val(&0u32));
+        assert_eq!(align_of_t(&[0isize, 1isize] as &[isize]), align_of_val(&[0isize, 1isize]));
+        assert_eq!(align_of_t(&[] as &[u8]), align_of_val::<[u8; 0]>(&[]));
+        assert_eq!(
+            align_of_t(NonNull::<u32>::dangling().as_ptr() as *const dyn std::fmt::Display),
+            align_of::<u32>()
+        );
+    }
+
+    #[test]
+    pub fn test_empty_slice() {
+        let slice_ptr = Vec::<char>::new().as_slice() as *const [char];
+        assert_valid_ptr(slice_ptr);
+    }
+
+    #[test]
+    pub fn test_empty_str() {
+        let slice_ptr = String::new().as_str() as *const str;
+        assert_valid_ptr(slice_ptr);
+    }
+
+    #[test]
+    fn test_dangling_zst() {
+        test_dangling_of_t::<()>();
+        test_dangling_of_t::<[(); 10]>();
+    }
+
+    fn test_dangling_of_t<T>() {
+        let dangling: *const T = NonNull::<T>::dangling().as_ptr();
+        assert_valid_ptr(dangling);
+
+        let vec_ptr = Vec::<T>::new().as_ptr();
+        assert_valid_ptr(vec_ptr);
+    }
+
+    #[test]
+    #[should_panic(expected = "Expected valid pointer, but found `null`")]
+    fn test_null_fat_ptr() {
+        assert_valid_ptr(ptr::null::<char>() as *const dyn Debug);
+    }
+
+    #[test]
+    #[should_panic(expected = "Expected valid pointer, but found `null`")]
+    fn test_null_char() {
+        assert_valid_ptr(ptr::null::<char>());
+    }
+}
+
\ 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..6b11e25840cd --- /dev/null +++ b/crates/doc/src/kani/models/mod.rs.html @@ -0,0 +1,455 @@ +mod.rs - source
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
+
// 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 + 7) / 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.clone())) },
+            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.clone())) };
+        assert_eq!(kani_mask, bitmask);
+    }
+}
+
\ No newline at end of file diff --git a/crates/doc/src/kani/slice.rs.html b/crates/doc/src/kani/slice.rs.html new file mode 100644 index 000000000000..809e55892c75 --- /dev/null +++ b/crates/doc/src/kani/slice.rs.html @@ -0,0 +1,69 @@ +slice.rs - source
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
+
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+use crate::{any, assume};
+
+/// 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:
+///
+/// ```rust
+/// 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
+/// ```
+pub fn any_slice_of_array<T, const LENGTH: usize>(arr: &[T; LENGTH]) -> &[T] {
+    let (from, to) = any_range::<LENGTH>();
+    &arr[from..to]
+}
+
+/// A mutable version of the previous function
+pub fn any_slice_of_array_mut<T, const LENGTH: usize>(arr: &mut [T; LENGTH]) -> &mut [T] {
+    let (from, to) = any_range::<LENGTH>();
+    &mut arr[from..to]
+}
+
+fn any_range<const LENGTH: usize>() -> (usize, usize) {
+    let from: usize = any();
+    let to: usize = any();
+    assume(to <= LENGTH);
+    assume(from <= to);
+    (from, to)
+}
+
\ No newline at end of file diff --git a/crates/doc/src/kani/tuple.rs.html b/crates/doc/src/kani/tuple.rs.html new file mode 100644 index 000000000000..c65de50662ce --- /dev/null +++ b/crates/doc/src/kani/tuple.rs.html @@ -0,0 +1,71 @@ +tuple.rs - source
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
+
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! Support for arbitrary tuples where each element implements
+//! `kani::Arbitrary`. Tuples of size up to 12 are supported in this
+//! file.
+
+use crate::Arbitrary;
+
+/// This macro implements `kani::Arbitrary` on a tuple whose elements
+/// already implement `kani::Arbitrary` by running `kani::any()` on
+/// each index of the tuple.
+macro_rules! tuple {
+    ($($typ:ident),*) => {
+        impl<$($typ : Arbitrary),*>  Arbitrary for ($($typ,)*) {
+            #[inline(always)]
+            fn any() -> Self {
+                ($(crate::any::<$typ>(),)*)
+            }
+        }
+    }
+}
+
+tuple!(A);
+tuple!(A, B);
+tuple!(A, B, C);
+tuple!(A, B, C, D);
+tuple!(A, B, C, D, E);
+tuple!(A, B, C, D, E, F);
+tuple!(A, B, C, D, E, F, G);
+tuple!(A, B, C, D, E, F, G, H);
+tuple!(A, B, C, D, E, F, G, H, I);
+tuple!(A, B, C, D, E, F, G, H, I, J);
+tuple!(A, B, C, D, E, F, G, H, I, J, K);
+tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
+
\ 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..a4174a10e7e7 --- /dev/null +++ b/crates/doc/src/kani/vec.rs.html @@ -0,0 +1,67 @@ +vec.rs - source
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
+
// Copyright Kani Contributors
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+use crate::{any, any_where, Arbitrary};
+
+/// 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,
+    [(); std::mem::size_of::<[T; MAX_LENGTH]>()]:,
+{
+    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,
+    [(); std::mem::size_of::<[T; EXACT_LENGTH]>()]:,
+{
+    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-23e9bde6c69aea69.txt b/crates/doc/static.files/COPYRIGHT-23e9bde6c69aea69.txt new file mode 100644 index 000000000000..1447df792f68 --- /dev/null +++ b/crates/doc/static.files/COPYRIGHT-23e9bde6c69aea69.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): + + 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/FiraSans-LICENSE-db4b642586e02d97.txt b/crates/doc/static.files/FiraSans-LICENSE-db4b642586e02d97.txt new file mode 100644 index 000000000000..d7e9c149b7ea --- /dev/null +++ b/crates/doc/static.files/FiraSans-LICENSE-db4b642586e02d97.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-8f9a781e4970d388.woff2 b/crates/doc/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 new file mode 100644 index 000000000000..7a1e5fc548ef Binary files /dev/null and b/crates/doc/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 differ diff --git a/crates/doc/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 b/crates/doc/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 new file mode 100644 index 000000000000..e766e06ccb0d Binary files /dev/null and b/crates/doc/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 differ diff --git a/crates/doc/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt b/crates/doc/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt new file mode 100644 index 000000000000..16fe87b06e80 --- /dev/null +++ b/crates/doc/static.files/LICENSE-APACHE-b91fa81cba47b86a.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-65090b722b3f6c56.txt b/crates/doc/static.files/LICENSE-MIT-65090b722b3f6c56.txt new file mode 100644 index 000000000000..31aa79387f27 --- /dev/null +++ b/crates/doc/static.files/LICENSE-MIT-65090b722b3f6c56.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-0f09457c7a19b7c6.ttf.woff2 b/crates/doc/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 new file mode 100644 index 000000000000..1866ad4bcea6 Binary files /dev/null and b/crates/doc/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 differ diff --git a/crates/doc/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt b/crates/doc/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt new file mode 100644 index 000000000000..4b3edc29eb90 --- /dev/null +++ b/crates/doc/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.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-1cc31594bf4f1f79.ttf.woff2 b/crates/doc/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 new file mode 100644 index 000000000000..462c34efcd9d Binary files /dev/null and b/crates/doc/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt b/crates/doc/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt new file mode 100644 index 000000000000..0d2941e148df --- /dev/null +++ b/crates/doc/static.files/SourceCodePro-LICENSE-d180d465a756484a.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-562dcc5011b6de7d.ttf.woff2 b/crates/doc/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 new file mode 100644 index 000000000000..10b558e0b69a Binary files /dev/null and b/crates/doc/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 b/crates/doc/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 new file mode 100644 index 000000000000..5ec64eef0ec9 Binary files /dev/null and b/crates/doc/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 b/crates/doc/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 new file mode 100644 index 000000000000..181a07f63bef Binary files /dev/null and b/crates/doc/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 b/crates/doc/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 new file mode 100644 index 000000000000..2ae08a7bedfe Binary files /dev/null and b/crates/doc/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 differ diff --git a/crates/doc/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md b/crates/doc/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md new file mode 100644 index 000000000000..175fa4f47aec --- /dev/null +++ b/crates/doc/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.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-46f98efaafac5295.ttf.woff2 b/crates/doc/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 new file mode 100644 index 000000000000..0263fc304226 Binary files /dev/null and b/crates/doc/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 differ diff --git a/crates/doc/static.files/favicon-2c020d218678b618.svg b/crates/doc/static.files/favicon-2c020d218678b618.svg new file mode 100644 index 000000000000..8b34b511989e --- /dev/null +++ b/crates/doc/static.files/favicon-2c020d218678b618.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/crates/doc/static.files/favicon-32x32-422f7d1d52889060.png b/crates/doc/static.files/favicon-32x32-422f7d1d52889060.png new file mode 100644 index 000000000000..69b8613ce150 Binary files /dev/null and b/crates/doc/static.files/favicon-32x32-422f7d1d52889060.png differ diff --git a/crates/doc/static.files/main-20a3ad099b048cf2.js b/crates/doc/static.files/main-20a3ad099b048cf2.js new file mode 100644 index 000000000000..133116e4d8c4 --- /dev/null +++ b/crates/doc/static.files/main-20a3ad099b048cf2.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")}function showMain(){removeClass(document.getElementById(MAIN_ID),"hidden")}function blurHandler(event,parentElem,hideCallback){if(!parentElem.contains(document.activeElement)&&!parentElem.contains(event.relatedTarget)){hideCallback()}}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")}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)}getSettingsButton().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={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(searchState.timeout!==null){clearTimeout(searchState.timeout);searchState.timeout=null}},isDisplayed:()=>searchState.outputElement().parentElement.id===ALTERNATIVE_DISPLAY_ID,focus:()=>{searchState.input.focus()},defocus:()=>{searchState.input.blur()},showResults:search=>{if(search===null||typeof search==="undefined"){search=searchState.outputElement()}switchDisplayedElement(search);searchState.mouseMovedAfterSearch=false;document.title=searchState.title},removeQueryParameters:()=>{document.title=searchState.titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.hash)}},hideResults:()=>{switchDisplayedElement(null);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=searchState.input;if(!searchState.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",()=>{search_input.origPlaceholder=search_input.placeholder;search_input.placeholder="Type your search here.";loadSearch()});if(search_input.value!==""){loadSearch()}const params=searchState.getQueryStringParams();if(params.search!==undefined){searchState.setLoadingSearch();loadSearch()}},setLoadingSearch:()=>{const search=searchState.outputElement();search.innerHTML="

"+searchState.loadingText+"

";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&&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 implElem=document.getElementById(implId);if(implElem&&implElem.parentElement.tagName==="SUMMARY"&&implElem.parentElement.parentElement.tagName==="DETAILS"){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)}},)}}}}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.getElementsByClassName("sidebar-elems")[0];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;if(path===current_page){link.className="current"}link.textContent=name;const li=document.createElement("li");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("opaque","opaque-types","Opaque Types");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.getElementsByClassName("sidebar-elems")[0];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.title="collapse all docs";innerToggle.children[0].innerText="\u2212"}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.title="expand all docs";innerToggle.children[0].innerText="+"}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=()=>{onEachLazy(document.getElementsByClassName("rust-example-rendered"),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.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");for(const node of line_numbers){parent.removeChild(node)}})};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{if(e.getAttribute("title")!==null){e.setAttribute("data-title",e.getAttribute("title"));e.removeAttribute("title")}if(e.getAttribute("data-title")!==null){const titleContent=document.createElement("div");titleContent.className="content";titleContent.appendChild(document.createTextNode(e.getAttribute("data-title")));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";const body=document.getElementsByTagName("body")[0];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"){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}const body=document.getElementsByTagName("body")[0];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){blurHandler(event,getHelpButton(),window.hidePopoverMenus)}function buildHelpMenu(){const book_info=document.createElement("span");const channel=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(".search-form .popover"),elem=>{elem.style.display="none"})};function getHelpMenu(buildNeeded){let menu=getHelpButton().querySelector(".popover");if(!menu&&buildNeeded){menu=buildHelpMenu()}return menu}function showHelp(){getHelpButton().querySelector("a").focus();const menu=getHelpMenu(true);if(menu.style.display==="none"){window.hideAllModals();menu.style.display=""}}if(isHelpPage){showHelp();document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault()})}else{document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||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");function hideSidebar(){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")}}function showSidebar(){if(isSrcPage){window.rustdocShowSourceSidebar()}else{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false")}}function changeSidebarSize(size){if(isSrcPage){updateLocalStorage("src-sidebar-width",size);sidebar.style.setProperty("--src-sidebar-width",size+"px");resizer.style.setProperty("--src-sidebar-width",size+"px")}else{updateLocalStorage("desktop-sidebar-width",size);sidebar.style.setProperty("--desktop-sidebar-width",size+"px");resizer.style.setProperty("--desktop-sidebar-width",size+"px")}}function isSidebarHidden(){return isSrcPage?!hasClass(document.documentElement,"src-sidebar-expanded"):hasClass(document.documentElement,"hide-sidebar")}function resize(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)}});function stopResize(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}}function initResize(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(){let reset_button_timeout=null;const but=document.getElementById("copy-path");if(!but){return}but.onclick=()=>{const parent=but.parentElement;const path=[];onEach(parent.childNodes,child=>{if(child.tagName==="A"){path.push(child.textContent)}});const el=document.createElement("textarea");el.value=path.join("::");el.setAttribute("readonly","");el.style.position="absolute";el.style.left="-9999px";document.body.appendChild(el);el.select();document.execCommand("copy");document.body.removeChild(el);but.classList.add("clicked");if(reset_button_timeout!==null){window.clearTimeout(reset_button_timeout)}function reset_button(){reset_button_timeout=null;but.classList.remove("clicked")}reset_button_timeout=window.setTimeout(reset_button,1000)}}()) \ No newline at end of file diff --git a/crates/doc/static.files/normalize-76eba96aa4d2e634.css b/crates/doc/static.files/normalize-76eba96aa4d2e634.css new file mode 100644 index 000000000000..469959f13729 --- /dev/null +++ b/crates/doc/static.files/normalize-76eba96aa4d2e634.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-df360f571f6edeae.css b/crates/doc/static.files/noscript-df360f571f6edeae.css new file mode 100644 index 000000000000..4c310ae52937 --- /dev/null +++ b/crates/doc/static.files/noscript-df360f571f6edeae.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%);--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;--settings-menu-filter:none;--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;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--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%);--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;--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;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--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-151179464ae7ed46.svg b/crates/doc/static.files/rust-logo-151179464ae7ed46.svg new file mode 100644 index 000000000000..62424d8ffd76 --- /dev/null +++ b/crates/doc/static.files/rust-logo-151179464ae7ed46.svg @@ -0,0 +1,61 @@ + + + diff --git a/crates/doc/static.files/rustdoc-dd39b87e5fcfba68.css b/crates/doc/static.files/rustdoc-dd39b87e5fcfba68.css new file mode 100644 index 000000000000..77f89832240d --- /dev/null +++ b/crates/doc/static.files/rustdoc-dd39b87e5fcfba68.css @@ -0,0 +1,46 @@ + :root{--nav-sub-mobile-padding:8px;--search-typename-width:6.75rem;--desktop-sidebar-width:200px;--src-sidebar-width:300px;--desktop-sidebar-z-index:100;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular-018c141bf0843ffd.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-8f9a781e4970d388.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-46f98efaafac5295.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-acdfaf1a8af734b1.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-a2c9cd1067f8b328.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'NanumBarunGothic';src:url("NanumBarunGothic-0f09457c7a19b7c6.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 "Source Serif 4",NanumBarunGothic,serif;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;flex-grow:1;overflow-wrap:break-word;overflow-wrap:anywhere;}.main-heading{display:flex;flex-wrap:wrap;padding-bottom:6px;margin-bottom:15px;}.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;}#crate-search,h1,h2,h3,h4,h5,h6,.sidebar,.mobile-topbar,.search-input,.search-results .result-name,.item-name>a,.out-of-band,span.since,a.src,#help-button>a,summary.hideme,.scraped-example-list,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,.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,a.test-arrow,.code-header{font-family:"Source Code Pro",monospace;}.docblock code,.docblock-short code{border-radius:3px;padding:0 0.125em;}.docblock pre code,.docblock-short 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 pre.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{padding:0;margin:0;list-style:none;}.sidebar-elems a,.sidebar>h2 a{display:block;padding:0.25rem;margin-left:-0.25rem;margin-right:0.25rem;}.sidebar h2{overflow-wrap:anywhere;padding:0;margin:0.7rem 0;}.sidebar h3{font-size:1.125rem;padding:0;margin:0;}.sidebar-elems,.sidebar>.version,.sidebar>h2{padding-left:24px;}.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 -16px 0 -16px;text-align:center;}.sidebar-crate h2 a{display:block;margin:0 calc(-24px + 0.25rem) 0 -0.2rem;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:last-child{margin-bottom:0px;}.rustdoc .example-wrap pre{margin:0;flex-grow:1;}.rustdoc:not(.src) .example-wrap pre{overflow:auto hidden;}.rustdoc .example-wrap pre.example-line-numbers,.rustdoc .example-wrap pre.src-line-numbers{flex-grow:0;min-width:fit-content;overflow:initial;text-align:right;-webkit-user-select:none;user-select:none;padding:14px 8px;color:var(--src-line-numbers-span-color);}.rustdoc .example-wrap pre.src-line-numbers{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;}.docblock-short{overflow-wrap:break-word;overflow-wrap:anywhere;}.docblock :not(pre)>code,.docblock-short 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:24px;position:relative;}.docblock>:not(.more-examples-toggle):not(.example-wrap){max-width:100%;overflow-x:auto;}.out-of-band{flex-grow:0;font-size:1.125rem;}.docblock code,.docblock-short code,pre,.rustdoc.src .example-wrap{background-color:var(--code-block-background-color);}#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);}div.where{white-space:pre-wrap;font-size:0.875rem;}.item-info{display:block;margin-left:24px;}.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 25px 0;display:flex;align-items:center;}.search-form{position:relative;display:flex;height:34px;flex-grow:1;}.src nav.sub{margin:0 0 15px 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:5px;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(.test-arrow):not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),.docblock-short a:not(.test-arrow):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{display:table;padding:0;margin:0;}.item-table>li{display:table-row;}.item-table>li>div{display:table-cell;}.item-table>li>.item-name{padding-right:1.25rem;}.search-results-title{margin-top:0;white-space:nowrap;display:flex;align-items:baseline;}#crate-search-div{position:relative;min-width:5em;}#crate-search{min-width:115px;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;}.search-results>a{display:flex;margin-left:2px;margin-right:2px;border-bottom:1px solid var(--search-result-border-color);gap:1em;}.search-results>a>div.desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:2;}.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;flex:3;}.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;}.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;}#help.popover{max-width:600px;--popover-arrow-offset:48px;}#help dt{float:left;clear:left;margin-right:0.5rem;}#help span.top,#help span.bottom{text-align:center;display:block;font-size:1.125rem;}#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>div{width:50%;float:left;padding:0 20px 20px 17px;}.item-info .stab{display:block;padding:3px;margin-bottom:5px;}.item-name .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{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;}a.test-arrow{visibility:hidden;position:absolute;padding:5px 10px 5px 10px;border-radius:5px;font-size:1.375rem;top:5px;right:5px;z-index:1;color:var(--test-arrow-color);background-color:var(--test-arrow-background-color);}a.test-arrow:hover{color:var(--test-arrow-hover-color);background-color:var(--test-arrow-hover-background-color);}.example-wrap:hover .test-arrow{visibility:visible;}.code-attribute{font-weight:300;color:var(--code-attribute-color);}.item-spacer{width:100%;height:12px;display:block;}.out-of-band>span.since{font-size:1.25rem;}.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;}: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";}.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{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{margin-left:4px;display:flex;}#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{display:flex;align-items:center;justify-content:center;background-color:var(--button-background-color);border:1px solid var(--border-color);border-radius:2px;color:var(--settings-button-color);font-size:20px;width:33px;}#settings-menu>a:hover,#settings-menu>a:focus,#help-button>a:hover,#help-button>a:focus,#sidebar-button>a:hover,#sidebar-button>a:focus{border-color:var(--settings-button-border-focus);}#settings-menu>a{line-height:0;font-size:0;}#settings-menu>a:before{content:url('data:image/svg+xml,\ + ');width:22px;height:22px;filter:var(--settings-menu-filter);}#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:34px;width:33px;margin-left:10px;padding:0;padding-left:2px;border:0;font-size:0;}#copy-path::before{filter:var(--copy-path-img-filter);content:url('data:image/svg+xml,\ +\ +\ +');width:19px;height:18px;}#copy-path:hover::before{filter:var(--copy-path-img-hover-filter);}#copy-path.clicked::before{content:url('data:image/svg+xml,\ + \ + ');}@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.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,') no-repeat top left;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{position:absolute;left:-24px;}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,') no-repeat top left;}details.toggle[open] >summary::after{content:"Collapse";}.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;}}@media (max-width:700px){*[id]{scroll-margin-top:45px;}.rustdoc{display:block;}main{padding-left:15px;padding-top:0px;}.main-heading{flex-direction:column;}.out-of-band{text-align:left;margin-left:initial;padding:initial;}.out-of-band .since::before{content:"Since ";}.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;}.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;}#copy-path,#help-button{display:none;}#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);}.item-table,.item-row,.item-table>li,.item-table>li>div,.search-results>a,.search-results>a>div{display:block;}.search-results>a{padding:5px 0px;}.search-results>a>div.desc,.item-table>li>div.desc{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.toggle:not(.top-doc)>summary{margin-left:10px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>details.toggle:not(.top-doc)>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}.impl-items>.item-info{margin-left:34px;}.src nav.sub{margin: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);}}@media print{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;}.docblock{margin-left:0;}main{padding:10px;}}@media (max-width:464px){.docblock{margin-left: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{position:relative;}.scraped-example .code-wrapper{position:relative;display:flex;flex-direction:row;flex-wrap:wrap;width:100%;}.scraped-example:not(.expanded) .code-wrapper{max-height:calc(1.5em * 5 + 10px);}.scraped-example:not(.expanded) .code-wrapper pre{overflow-y:hidden;padding-bottom:0;max-height:calc(1.5em * 5 + 10px);}.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre{max-height:calc(1.5em * 10 + 10px);}.scraped-example .code-wrapper .next,.scraped-example .code-wrapper .prev,.scraped-example .code-wrapper .expand{color:var(--main-color);position:absolute;top:0.25em;z-index:1;padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.scraped-example .code-wrapper .prev{right:2.25em;}.scraped-example .code-wrapper .next{right:1.25em;}.scraped-example .code-wrapper .expand{right:0.25em;}.scraped-example:not(.expanded) .code-wrapper::before,.scraped-example:not(.expanded) .code-wrapper::after{content:" ";width:100%;height:5px;position:absolute;z-index:1;}.scraped-example:not(.expanded) .code-wrapper::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) .code-wrapper::after{bottom:0;background:linear-gradient(to top,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example .code-wrapper .example-wrap{width:100%;overflow-y:hidden;margin-bottom:0;}.scraped-example:not(.expanded) .code-wrapper .example-wrap{overflow-x:hidden;}.scraped-example .example-wrap .rust span.highlight{background:var(--scrape-example-code-line-highlight);}.scraped-example .example-wrap .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%);--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;--settings-menu-filter:none;--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;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--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%);--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:none;--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;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--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%);--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(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);--test-arrow-color:#788797;--test-arrow-background-color:rgba(57,175,215,0.09);--test-arrow-hover-color:#c5c5c5;--test-arrow-hover-background-color:rgba(57,175,215,0.368);--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-ef1e698c1d417c0c.js b/crates/doc/static.files/scrape-examples-ef1e698c1d417c0c.js new file mode 100644 index 000000000000..ba830e3744a4 --- /dev/null +++ b/crates/doc/static.files/scrape-examples-ef1e698c1d417c0c.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");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 wrapper=elt.querySelector(".code-wrapper");const halfHeight=wrapper.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.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset)}function updateScrapedExample(example,isHidden){const locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");if(locs.length>1){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};example.querySelector(".prev").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length})});example.querySelector(".next").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length})})}const expandButton=example.querySelector(".expand");if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");scrollToLoc(example,locs[0][0],isHidden)}else{addClass(example,"expanded")}})}scrollToLoc(example,locs[0][0],isHidden)}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>updateScrapedExample(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=>updateScrapedExample(el,true))})},{once:true})})})() \ No newline at end of file diff --git a/crates/doc/static.files/search-d52510db62a78183.js b/crates/doc/static.files/search-d52510db62a78183.js new file mode 100644 index 000000000000..a2824f297f66 --- /dev/null +++ b/crates/doc/static.files/search-d52510db62a78183.js @@ -0,0 +1,5 @@ +"use strict";if(!Array.prototype.toSpliced){Array.prototype.toSpliced=function(){const me=this.slice();Array.prototype.splice.apply(me,arguments);return me}}(function(){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 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",];const TY_GENERIC=itemTypes.indexOf("generic");const TY_IMPORT=itemTypes.indexOf("import");const ROOT_PATH=typeof window!=="undefined"?window.rootPath:"../";const UNBOXING_LIMIT=5;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)}}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 initSearch(rawSearchIndex){const MAX_RESULTS=200;const NO_TYPE_FILTER=-1;let searchIndex;let searchIndexDeprecated;let searchIndexEmptyDesc;let functionTypeFingerprint;let currentResults;const typeNameIdMap=new Map();const ALIASES=new Map();const typeNameIdOfArray=buildTypeMapIndex("array");const typeNameIdOfSlice=buildTypeMapIndex("slice");const typeNameIdOfArrayOrSlice=buildTypeMapIndex("[]");const typeNameIdOfTuple=buildTypeMapIndex("tuple");const typeNameIdOfUnit=buildTypeMapIndex("unit");const typeNameIdOfTupleOrUnit=buildTypeMapIndex("()");const typeNameIdOfFn=buildTypeMapIndex("fn");const typeNameIdOfFnMut=buildTypeMapIndex("fnmut");const typeNameIdOfFnOnce=buildTypeMapIndex("fnonce");const typeNameIdOfHof=buildTypeMapIndex("->");function buildTypeMapIndex(name,isAssocType){if(name===""||name===null){return null}if(typeNameIdMap.has(name)){const obj=typeNameIdMap.get(name);obj.assocOnly=isAssocType&&obj.assocOnly;return obj.id}else{const id=typeNameIdMap.size;typeNameIdMap.set(name,{id,assocOnly:isAssocType});return id}}function isSpecialStartCharacter(c){return"<\"".indexOf(c)!==-1}function isEndCharacter(c){return"=,>-])".indexOf(c)!==-1}function itemTypeFromName(typename){const index=itemTypes.findIndex(i=>i===typename);if(index<0){throw["Unknown type filter ",typename]}return index}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 isPathStart(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="::"}function isReturnArrow(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="->"}function isIdentCharacter(c){return(c==="_"||(c>="0"&&c<="9")||(c>="a"&&c<="z")||(c>="A"&&c<="Z"))}function isSeparatorCharacter(c){return c===","||c==="="}function isPathSeparator(c){return c===":"||c===" "}function prevIs(parserState,lookingFor){let pos=parserState.pos;while(pos>0){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 skipWhitespace(parserState){while(parserState.pos0){throw["Cannot have more than one element if you use quotes"]}const typeFilter=parserState.typeFilter;parserState.typeFilter=null;if(name==="!"){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(path.endsWith("::")){throw["Paths cannot end with ","::"]}else if(quadcolon!==null){throw["Unexpected ",quadcolon[0]]}const pathSegments=path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/);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,gen.bindingName.generics);return false}return true}),bindings,typeFilter,bindingName,}}function getIdentEndPosition(parserState){const start=parserState.pos;let end=parserState.pos;let foundExclamation=-1;while(parserState.pos0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]]}else{throw["Unexpected ",c]}}parserState.pos+=1;end=parserState.pos}if(foundExclamation!==-1&&foundExclamation!==start&&isIdentCharacter(parserState.userQuery[foundExclamation-1])){if(parserState.typeFilter===null){parserState.typeFilter="macro"}else if(parserState.typeFilter!=="macro"){throw["Invalid search type: macro ","!"," and ",parserState.typeFilter," both specified",]}end=foundExclamation}return end}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.name;parserState.pos+=1;parserState.totalElems-=1;query.literalSearch=false;getNextElem(query,parserState,elems,isInGenerics)}}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 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 checkExtraTypeFilterCharacters(start,parserState){const query=parserState.userQuery.slice(start,parserState.pos).trim();for(const c in query){if(!isIdentCharacter(query[c])){throw["Unexpected ",query[c]," in type filter (before ",":",")",]}}}function parseInput(query,parserState){let foundStopChar=true;while(parserState.pos"){if(isReturnArrow(parserState)){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.pos"]}break}else{parserState.pos+=1}}}function newParsedQuery(userQuery){return{original:userQuery,userQuery:userQuery.toLowerCase(),elems:[],returned:[],foundElems:0,totalElems:0,literalSearch:false,error:null,correction:null,proposeCorrectionFrom:null,proposeCorrectionTo:null,typeFingerprint:new Uint32Array(4),}}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"&&rawSearchIndex.has(elem.value)){return elem.value}return null}function parseQuery(userQuery){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)}}}userQuery=userQuery.trim().replace(/\r|\n|\t/g," ");const parserState={length:userQuery.length,pos:0,totalElems:0,genericsElems:0,typeFilter:null,isInBinding:null,userQuery:userQuery.toLowerCase(),};let query=newParsedQuery(userQuery);try{parseInput(query,parserState);for(const elem of query.elems){convertTypeFilterOnElem(elem)}for(const elem of query.returned){convertTypeFilterOnElem(elem)}}catch(err){query=newParsedQuery(userQuery);query.error=err;return query}if(!query.literalSearch){query.literalSearch=parserState.totalElems>1}query.foundElems=query.elems.length+query.returned.length;query.totalElems=parserState.totalElems;return query}function createQueryResults(results_in_args,results_returned,results_others,parsedQuery){return{"in_args":results_in_args,"returned":results_returned,"others":results_others,"query":parsedQuery,}}async function execQuery(parsedQuery,filterCrates,currentCrate){const results_others=new Map(),results_in_args=new Map(),results_returned=new Map();function transformResults(results){const duplicates=new Set();const out=[];for(const result of results){if(result.id!==-1){const obj=searchIndex[result.id];obj.dist=result.dist;const res=buildHrefAndPath(obj);obj.displayPath=pathSplitter(res[0]);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]);obj.href=res[1];out.push(obj);if(out.length>=MAX_RESULTS){break}}}return out}async function sortResults(results,isType,preferredCrate){const userQuery=parsedQuery.userQuery;const result_list=[];for(const result of results.values()){result.item=searchIndex[result.id];result.word=searchIndex[result.id].word;result_list.push(result)}result_list.sort((aaa,bbb)=>{let a,b;a=(aaa.word!==userQuery);b=(bbb.word!==userQuery);if(a!==b){return a-b}a=(aaa.index<0);b=(bbb.index<0);if(a!==b){return a-b}a=aaa.path_dist;b=bbb.path_dist;if(a!==b){return a-b}a=aaa.index;b=bbb.index;if(a!==b){return a-b}a=(aaa.dist);b=(bbb.dist);if(a!==b){return a-b}a=searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex);b=searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex);if(a!==b){return a-b}a=(aaa.item.crate!==preferredCrate);b=(bbb.item.crate!==preferredCrate);if(a!==b){return a-b}a=aaa.word.length;b=bbb.word.length;if(a!==b){return a-b}a=aaa.word;b=bbb.word;if(a!==b){return(a>b?+1:-1)}a=searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex);b=searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex);if(a!==b){return a-b}a=aaa.item.ty;b=bbb.item.ty;if(a!==b){return a-b}a=aaa.item.path;b=bbb.item.path;if(a!==b){return(a>b?+1:-1)}return 0});return transformResults(result_list)}function unifyFunctionTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb,unboxingDepth,){if(unboxingDepth>=UNBOXING_LIMIT){return false}const mgens=mgensIn===null?null:new Map(mgensIn);if(queryElems.length===0){return!solutionCb||solutionCb(mgens)}if(!fnTypesIn||fnTypesIn.length===0){return false}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 fnType of fnTypesIn){if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens)){continue}if(fnType.id<0&&queryElem.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==queryElem.id){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,queryElem.id);if(!solutionCb||solutionCb(mgensScratch)){return true}}else if(!solutionCb||solutionCb(mgens?new Map(mgens):null)){return true}}for(const fnType of fnTypesIn){if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1,)){continue}if(fnType.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,0);if(unifyFunctionTypes(whereClause[(-fnType.id)-1],queryElems,whereClause,mgensScratch,solutionCb,unboxingDepth+1,)){return true}}else if(unifyFunctionTypes([...fnType.generics,...Array.from(fnType.bindings.values()).flat()],queryElems,whereClause,mgens?new Map(mgens):null,solutionCb,unboxingDepth+1,)){return true}}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<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==queryElem.id){continue}mgensScratch.set(fnType.id,queryElem.id)}else{mgensScratch=mgens}fnTypes[i]=fnTypes[flast];fnTypes.length=flast;if(!queryElemsTmp){queryElemsTmp=queryElems.slice(0,qlast)}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||solutionCb(mgensScratch)}const solution=unifyFunctionTypeCheckBindings(fnType,queryElem,whereClause,mgensScratch,unboxingDepth,);if(!solution){return false}const simplifiedGenerics=solution.simplifiedGenerics;for(const simplifiedMgens of solution.mgens){const passesUnification=unifyFunctionTypes(simplifiedGenerics,queryElem.generics,whereClause,simplifiedMgens,solutionCb,unboxingDepth,);if(passesUnification){return true}}return false},unboxingDepth,);if(passesUnification){return true}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}let mgensScratch;if(fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==0){continue}mgensScratch.set(fnType.id,0)}else{mgensScratch=mgens}const generics=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,...generics,...bindings),queryElems,whereClause,mgensScratch,solutionCb,unboxingDepth+1,);if(passesUnification){return true}}return false}function unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgensIn){if(!typePassesFilter(queryElem.typeFilter,fnType.ty)){return false}if(fnType.id<0&&queryElem.id<0){if(mgensIn){if(mgensIn.has(fnType.id)&&mgensIn.get(fnType.id)!==queryElem.id){return false}for(const[fid,qid]of mgensIn.entries()){if(fnType.id!==fid&&queryElem.id===qid){return false}if(fnType.id===fid&&queryElem.id!==qid){return false}}}return true}else{if(queryElem.id===typeNameIdOfArrayOrSlice&&(fnType.id===typeNameIdOfSlice||fnType.id===typeNameIdOfArray)){}else if(queryElem.id===typeNameIdOfTupleOrUnit&&(fnType.id===typeNameIdOfTuple||fnType.id===typeNameIdOfUnit)){}else if(queryElem.id===typeNameIdOfHof&&(fnType.id===typeNameIdOfFn||fnType.id===typeNameIdOfFnMut||fnType.id===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=[...simplifiedGenerics,...binds]}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<0&&queryElem.id>=0){if(!whereClause){return false}if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){return false}const mgensTmp=new Map(mgens);mgensTmp.set(fnType.id,null);return checkIfInList(whereClause[(-fnType.id)-1],queryElem,whereClause,mgensTmp,unboxingDepth,)}else if(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 checkIfInList(list,elem,whereClause,mgens,unboxingDepth){for(const entry of list){if(checkType(entry,elem,whereClause,mgens,unboxingDepth)){return true}}return false}function checkType(row,elem,whereClause,mgens,unboxingDepth){if(unboxingDepth>=UNBOXING_LIMIT){return false}if(row.bindings.size===0&&elem.bindings.size===0){if(elem.id<0&&mgens===null){return row.id<0||checkIfInList(row.generics,elem,whereClause,mgens,unboxingDepth+1,)}if(row.id>0&&elem.id>0&&elem.pathWithoutLast.length===0&&typePassesFilter(elem.typeFilter,row.ty)&&elem.generics.length===0&&elem.id!==typeNameIdOfArrayOrSlice&&elem.id!==typeNameIdOfTupleOrUnit&&elem.id!==typeNameIdOfHof){return row.id===elem.id||checkIfInList(row.generics,elem,whereClause,mgens,unboxingDepth,)}}return unifyFunctionTypes([row],[elem],whereClause,mgens,null,unboxingDepth)}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,}}function handleAliases(ret,query,filterCrates,currentCrate){const lowerQuery=query.toLowerCase();const aliases=[];const crateAliases=[];if(filterCrates!==null){if(ALIASES.has(filterCrates)&&ALIASES.get(filterCrates).has(lowerQuery)){const query_aliases=ALIASES.get(filterCrates).get(lowerQuery);for(const alias of query_aliases){aliases.push(createAliasFromItem(searchIndex[alias]))}}}else{for(const[crate,crateAliasesIndex]of 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(searchIndex[alias]))}}}}const sortFunc=(aaa,bbb)=>{if(aaa.path{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(pushFunc);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 handleSingleArg(row,pos,elem,results_others,results_in_args,results_returned,maxEditDistance,){if(!row||(filterCrates!==null&&row.crate!==filterCrates)){return}let path_dist=0;const fullId=row.id;const tfpDist=compareTypeFingerprints(fullId,parsedQuery.typeFingerprint,);if(tfpDist!==null){const in_args=row.type&&row.type.inputs&&checkIfInList(row.type.inputs,elem,row.type.where_clause,null,0);const returned=row.type&&row.type.output&&checkIfInList(row.type.output,elem,row.type.where_clause,null,0);if(in_args){results_in_args.max_dist=Math.max(results_in_args.max_dist||0,tfpDist);const maxDist=results_in_args.sizenormalizedIndex&&normalizedIndex!==-1)){index=normalizedIndex}if(elem.fullPath.length>1){path_dist=checkPath(elem.pathWithoutLast,row);if(path_dist===null){return}}if(parsedQuery.literalSearch){if(row.word===elem.pathLast){addIntoResults(results_others,fullId,pos,index,0,path_dist)}return}const dist=editDistance(row.normalizedName,elem.normalizedPathLast,maxEditDistance);if(index===-1&&dist>maxEditDistance){return}addIntoResults(results_others,fullId,pos,index,dist,path_dist,maxEditDistance)}function handleArgs(row,pos,results){if(!row||(filterCrates!==null&&row.crate!==filterCrates)||!row.type){return}const tfpDist=compareTypeFingerprints(row.id,parsedQuery.typeFingerprint,);if(tfpDist===null){return}if(results.size>=MAX_RESULTS&&tfpDist>results.max_dist){return}if(!unifyFunctionTypes(row.type.inputs,parsedQuery.elems,row.type.where_clause,null,mgens=>{return unifyFunctionTypes(row.type.output,parsedQuery.returned,row.type.where_clause,mgens,null,0,)},0,)){return}results.max_dist=Math.max(results.max_dist||0,tfpDist);addIntoResults(results,row.id,pos,0,tfpDist,0,Number.MAX_VALUE)}function innerRunQuery(){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();function convertNameToId(elem,isAssocType){if(typeNameIdMap.has(elem.normalizedPathLast)&&(isAssocType||!typeNameIdMap.get(elem.normalizedPathLast).assocOnly)){elem.id=typeNameIdMap.get(elem.normalizedPathLast).id}else if(!parsedQuery.literalSearch){let match=null;let matchDist=maxEditDistance+1;let matchName="";for(const[name,{id,assocOnly}]of typeNameIdMap){const dist=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.name)){elem.id=genericSymbols.get(elem.name)}else{elem.id=-(genericSymbols.size+1);genericSymbols.set(elem.name,elem.id)}if(elem.typeFilter===-1&&elem.name.length>=3){const maxPartDistance=Math.floor(elem.name.length/3);let matchDist=maxPartDistance+1;let matchName="";for(const name of typeNameIdMap.keys()){const dist=editDistance(name,elem.name,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(!typeNameIdMap.has(name)){parsedQuery.error=["Type parameter ",name," does not exist",];return[null,[]]}for(const elem2 of constraints){convertNameToId(elem2)}return[typeNameIdMap.get(name).id,constraints]}),)}const fps=new Set();for(const elem of parsedQuery.elems){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}for(const elem of parsedQuery.returned){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}if(parsedQuery.foundElems===1&&parsedQuery.returned.length===0){if(parsedQuery.elems.length===1){const elem=parsedQuery.elems[0];for(let i=0,nSearchIndex=searchIndex.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=searchIndex.length;i{const descs=await Promise.all(list.map(result=>{return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex)?"":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}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()}}function 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=ROOT_PATH+path.replace(/::/g,"/")+"/"+name+"/index.html"}else if(type==="import"){displayPath=item.path+"::";href=ROOT_PATH+item.path.replace(/::/g,"/")+"/index.html#reexport."+name}else if(type==="primitive"||type==="keyword"){displayPath="";href=ROOT_PATH+path.replace(/::/g,"/")+"/"+type+"."+name+".html"}else if(type==="externcrate"){displayPath="";href=ROOT_PATH+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+"::"}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=ROOT_PATH+path.replace(/::/g,"/")+"/"+pageType+"."+pageName+".html#"+anchor}else{displayPath=item.path+"::";href=ROOT_PATH+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}async function addTab(array,query,display){const extraClass=display?" active":"";const output=document.createElement("div");if(array.length>0){output.className="search-results "+extraClass;for(const item of array){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("div");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);link.appendChild(description);output.appendChild(link)}}else if(query.error===null){output.className="search-failed"+extraClass;output.innerHTML="No results :(
"+"Try on DuckDuckGo?

"+"Or try looking in one of these:"}return[output,array.length]}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=parseQuery(searchState.input.value)}currentResults=results.query.userQuery;const[ret_others,ret_in_args,ret_returned]=await Promise.all([addTab(results.others,results.query,true),addTab(results.in_args,results.query,false),addTab(results.returned,results.query,false),]);let currentTab=searchState.currentTab;if((currentTab===0&&ret_others[1]===0)||(currentTab===1&&ret_in_args[1]===0)||(currentTab===2&&ret_returned[1]===0)){if(ret_others[1]!==0){currentTab=0}else if(ret_in_args[1]!==0){currentTab=1}else if(ret_returned[1]!==0){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",ret_others[1])+"
";currentTab=0}else if(results.query.foundElems<=1&&results.query.returned.length===0){output+="
"+makeTabHeader(0,"In Names",ret_others[1])+makeTabHeader(1,"In Parameters",ret_in_args[1])+makeTabHeader(2,"In Return Types",ret_returned[1])+"
"}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,ret_others[1])+"
";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 resultsElem=document.createElement("div");resultsElem.id="results";resultsElem.appendChild(ret_others[0]);resultsElem.appendChild(ret_in_args[0]);resultsElem.appendChild(ret_returned[0]);search.innerHTML=output;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=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="Results for "+query.original+" - Rust";updateSearchHistory(buildUrl(query.original,filterCrates));await showResults(await execQuery(query,filterCrates,window.currentCrate),params.go_to_first,filterCrates)}function buildItemSearchTypeAll(types,lowercasePaths){return types.length>0?types.map(type=>buildItemSearchType(type,lowercasePaths)):EMPTY_GENERICS_ARRAY}const EMPTY_BINDINGS_MAP=new Map();const EMPTY_GENERICS_ARRAY=[];let TYPES_POOL=new Map();function buildItemSearchType(type,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=EMPTY_GENERICS_ARRAY;bindings=EMPTY_BINDINGS_MAP}else{pathIndex=type[PATH_INDEX_DATA];generics=buildItemSearchTypeAll(type[GENERICS_DATA],lowercasePaths,);if(type.length>BINDINGS_DATA&&type[BINDINGS_DATA].length>0){bindings=new Map(type[BINDINGS_DATA].map(binding=>{const[assocType,constraints]=binding;return[buildItemSearchType(assocType,lowercasePaths,true).id,buildItemSearchTypeAll(constraints,lowercasePaths),]}))}else{bindings=EMPTY_BINDINGS_MAP}}let result;if(pathIndex<0){result={id:pathIndex,ty:TY_GENERIC,path:null,exactPath:null,generics,bindings,}}else if(pathIndex===0){result={id:null,ty:null,path:null,exactPath:null,generics,bindings,}}else{const item=lowercasePaths[pathIndex-1];result={id:buildTypeMapIndex(item.name,isAssocType),ty:item.ty,path:item.path,exactPath:item.exactPath,generics,bindings,}}const cr=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){return cr}}TYPES_POOL.set(result.id,result);return result}function buildFunctionSearchTypeCallback(lowercasePaths){return functionSearchType=>{if(functionSearchType===0){return null}const INPUTS_DATA=0;const OUTPUT_DATA=1;let inputs,output;if(typeof functionSearchType[INPUTS_DATA]==="number"){inputs=[buildItemSearchType(functionSearchType[INPUTS_DATA],lowercasePaths)]}else{inputs=buildItemSearchTypeAll(functionSearchType[INPUTS_DATA],lowercasePaths,)}if(functionSearchType.length>1){if(typeof functionSearchType[OUTPUT_DATA]==="number"){output=[buildItemSearchType(functionSearchType[OUTPUT_DATA],lowercasePaths)]}else{output=buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA],lowercasePaths,)}}else{output=[]}const where_clause=[];const l=functionSearchType.length;for(let i=2;i{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));fps.add(input)}for(const g of type.generics){buildFunctionTypeFingerprint(g,output,fps)}const fb={id:null,ty:0,generics:EMPTY_GENERICS_ARRAY,bindings:EMPTY_BINDINGS_MAP,};for(const[k,v]of type.bindings.entries()){fb.id=k;fb.generics=v;buildFunctionTypeFingerprint(fb,output,fps)}output[3]=fps.size}function compareTypeFingerprints(fullId,queryFingerprint){const fh0=functionTypeFingerprint[fullId*4];const fh1=functionTypeFingerprint[(fullId*4)+1];const fh2=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 functionTypeFingerprint[(fullId*4)+3]}class VlqHexDecoder{constructor(string,cons){this.string=string;this.cons=cons;this.offset=0;this.backrefQueue=[]}decodeList(){const cb="}".charCodeAt(0);let c=this.string.charCodeAt(this.offset);const ret=[];while(c!==cb){ret.push(this.decode());c=this.string.charCodeAt(this.offset)}this.offset+=1;return ret}decode(){const[ob,la]=["{","`"].map(c=>c.charCodeAt(0));let n=0;let c=this.string.charCodeAt(this.offset);if(c===ob){this.offset+=1;return this.decodeList()}while(c>1];this.offset+=1;return sign?-value:value}next(){const c=this.string.charCodeAt(this.offset);const[zero,ua,la]=["0","@","`"].map(c=>c.charCodeAt(0));if(c>=zero&&c16){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;for(let i=0;i=start&&value<=(start+lenm1)){return true}}return false}}class RoaringBitmapArray{constructor(cardinality,array){this.cardinality=cardinality;this.array=array}contains(value){const l=this.cardinality*2;for(let i=0;i>3]&(1<<(value&7)))}}function buildIndex(rawSearchIndex){searchIndex=[];searchIndexDeprecated=new Map();searchIndexEmptyDesc=new Map();const charA="A".charCodeAt(0);let currentIndex=0;let id=0;for(const crate of rawSearchIndex.values()){id+=crate.t.length+1}functionTypeFingerprint=new Uint32Array((id+1)*4);id=0;for(const[crate,crateCorpus]of rawSearchIndex){const itemDescShardDecoder=new VlqHexDecoder(crateCorpus.D,noop=>noop);let descShard={crate,shard:0,start:0,len:itemDescShardDecoder.next(),promise:null,resolve:null,};const descShardList=[descShard];searchIndexDeprecated.set(crate,new RoaringBitmap(crateCorpus.c));searchIndexEmptyDesc.set(crate,new RoaringBitmap(crateCorpus.e));let descIndex=0;const crateRow={crate,ty:3,name:crate,path:"",descShard,descIndex,exactPath:"",desc:crateCorpus.doc,parent:undefined,type:null,id,word:crate,normalizedName:crate.indexOf("_")===-1?crate:crate.replace(/_/g,""),bitIndex:0,implDisambiguator:null,};id+=1;searchIndex.push(crateRow);currentIndex+=1;if(!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 itemParentIdxs=crateCorpus.i;const implDisambiguator=new Map(crateCorpus.b);const paths=crateCorpus.p;const aliases=crateCorpus.a;const lowercasePaths=[];const itemFunctionDecoder=new VlqHexDecoder(crateCorpus.f,buildFunctionSearchTypeCallback(lowercasePaths),);let len=paths.length;let lastPath=itemPaths.get(0);for(let i=0;i2){path=itemPaths.has(elem[2])?itemPaths.get(elem[2]):lastPath;lastPath=path}const exactPath=elem.length>3?itemPaths.get(elem[3]):path;lowercasePaths.push({ty,name:name.toLowerCase(),path,exactPath});paths[i]={ty,name,path,exactPath}}lastPath="";len=itemTypes.length;for(let i=0;i=descShard.len&&!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)}let word="";if(typeof itemNames[i]==="string"){word=itemNames[i].toLowerCase()}const path=itemPaths.has(i)?itemPaths.get(i):lastPath;const type=itemFunctionDecoder.next();if(type!==null){if(type){const fp=functionTypeFingerprint.subarray(id*4,(id+1)*4);const fps=new Set();for(const t of type.inputs){buildFunctionTypeFingerprint(t,fp,fps)}for(const t of type.output){buildFunctionTypeFingerprint(t,fp,fps)}for(const w of type.where_clause){for(const t of w){buildFunctionTypeFingerprint(t,fp,fps)}}}}const row={crate,ty:itemTypes.charCodeAt(i)-charA,name:itemNames[i],path,descShard,descIndex,exactPath:itemReexports.has(i)?itemPaths.get(itemReexports.get(i)):path,parent:itemParentIdxs[i]>0?paths[itemParentIdxs[i]-1]:undefined,type,id,word,normalizedName:word.indexOf("_")===-1?word:word.replace(/_/g,""),bitIndex,implDisambiguator:implDisambiguator.has(i)?implDisambiguator.get(i):null,};id+=1;searchIndex.push(row);lastPath=row.path;if(!searchIndexEmptyDesc.get(crate).contains(bitIndex)){descIndex+=1}}if(aliases){const currentCrateAliases=new Map();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;searchState.descShards.set(crate,descShardList)}TYPES_POOL=new Map()}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)}buildIndex(rawSearchIndex);if(typeof window!=="undefined"){registerSearchEvents();if(window.searchState.getQueryStringParams().search){search()}}if(typeof exports!=="undefined"){exports.initSearch=initSearch;exports.execQuery=execQuery;exports.parseQuery=parseQuery}}if(typeof window!=="undefined"){window.initSearch=initSearch;if(window.searchIndex!==undefined){initSearch(window.searchIndex)}}else{initSearch(new Map())}})() \ No newline at end of file diff --git a/crates/doc/static.files/settings-4313503d2e1961c2.js b/crates/doc/static.files/settings-4313503d2e1961c2.js new file mode 100644 index 000000000000..ab425fe49d50 --- /dev/null +++ b/crates/doc/static.files/settings-4313503d2e1961c2.js @@ -0,0 +1,17 @@ +"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem)}updateLocalStorage(settingName,value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){window.rustdoc_add_line_numbers_to_examples()}else{window.rustdoc_remove_line_numbers_from_examples()}break;case"hide-sidebar":if(value===true){addClass(document.documentElement,"hide-sidebar")}else{removeClass(document.documentElement,"hide-sidebar")}break}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden")}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden")}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark()}else{hideLightAndDark()}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{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){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":"Disable keyboard shortcuts","js_name":"disable-shortcuts","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){blurHandler(event,getSettingsButton(),window.hidePopoverMenus)}if(isSettingsPage){getSettingsButton().onclick=event=>{event.preventDefault()}}else{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-e66d777a5a92e9b2.js b/crates/doc/static.files/src-script-e66d777a5a92e9b2.js new file mode 100644 index 000000000000..d0aebb85103b --- /dev/null +++ b/crates/doc/static.files/src-script-e66d777a5a92e9b2.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-118b08c4c78b968e.js b/crates/doc/static.files/storage-118b08c4c78b968e.js new file mode 100644 index 000000000000..98189467745a --- /dev/null +++ b/crates/doc/static.files/storage-118b08c4c78b968e.js @@ -0,0 +1,24 @@ +"use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=document.getElementById("themeStyle");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{window.localStorage.setItem("rustdoc-"+name,value)}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name)}catch(e){return null}}const getVar=(function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.attributes["data-"+name].value:null});function switchTheme(newThemeName,saveTheme){const themeNames=getVar("themes").split(",").filter(t=>t);themeNames.push(...builtinThemes);if(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.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=document.getElementById("themeStyle")}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&&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")}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) \ 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..c3e9910059e9 --- /dev/null +++ b/crates/doc/trait.impl/core/default/trait.Default.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[["impl Default for RoundRobin"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ 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..25f17d6acc35 --- /dev/null +++ b/crates/doc/trait.impl/core/future/future/trait.Future.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[["impl Future for JoinHandle"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ 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..fbcbbb106180 --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[["impl Freeze for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl Freeze for JoinHandle",1,["kani::futures::JoinHandle"]],["impl Freeze for RoundRobin",1,["kani::futures::RoundRobin"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ 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..8afa25a042ba --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[["impl Send for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl Send for JoinHandle",1,["kani::futures::JoinHandle"]],["impl Send for RoundRobin",1,["kani::futures::RoundRobin"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ 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..35dd68cca33f --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[["impl Sync for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl Sync for JoinHandle",1,["kani::futures::JoinHandle"]],["impl Sync for RoundRobin",1,["kani::futures::RoundRobin"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ 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..606cc814d17c --- /dev/null +++ b/crates/doc/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[["impl Unpin for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl Unpin for JoinHandle",1,["kani::futures::JoinHandle"]],["impl Unpin for RoundRobin",1,["kani::futures::RoundRobin"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ 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..05637495e7f8 --- /dev/null +++ b/crates/doc/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[["impl RefUnwindSafe for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl RefUnwindSafe for JoinHandle",1,["kani::futures::JoinHandle"]],["impl RefUnwindSafe for RoundRobin",1,["kani::futures::RoundRobin"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ 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..c0290de20afd --- /dev/null +++ b/crates/doc/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[["impl UnwindSafe for SchedulingAssumption",1,["kani::futures::SchedulingAssumption"]],["impl UnwindSafe for JoinHandle",1,["kani::futures::JoinHandle"]],["impl UnwindSafe for RoundRobin",1,["kani::futures::RoundRobin"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/crates/doc/trait.impl/kani/arbitrary/trait.Arbitrary.js b/crates/doc/trait.impl/kani/arbitrary/trait.Arbitrary.js new file mode 100644 index 000000000000..13b2fe0e3d3a --- /dev/null +++ b/crates/doc/trait.impl/kani/arbitrary/trait.Arbitrary.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ 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..13b2fe0e3d3a --- /dev/null +++ b/crates/doc/trait.impl/kani/futures/trait.SchedulingStrategy.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"kani":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/crates/index.html b/crates/index.html new file mode 100644 index 000000000000..d0e69369bcf2 --- /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/debugging-verification-failures.html b/debugging-verification-failures.html new file mode 100644 index 000000000000..d31889b5e6cf --- /dev/null +++ b/debugging-verification-failures.html @@ -0,0 +1,266 @@ + + + + + + Debugging verification failures - The Kani Rust Verifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+
+

Debugging verification failures

+

When the result of a certain check comes back as a FAILURE, +Kani offers different options to help debug:

+
    +
  • --concrete-playback. This experimental feature generates a Rust unit test case that plays back a failing +proof harness using a concrete counterexample.
  • +
  • --visualize. This feature generates an HTML text-based trace that +enumerates the execution steps leading to the check failure.
  • +
+

Concrete playback

+

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/dev-assess.html b/dev-assess.html new file mode 100644 index 000000000000..cef1893a1a5f --- /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..a93af3f6f5fd --- /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..254dd7a4d05e --- /dev/null +++ b/getting-started.html @@ -0,0 +1,194 @@ + + + + + + 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.

+

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..72653cf4dc8f --- /dev/null +++ b/getting-started/verification-results/src/main.rs @@ -0,0 +1,97 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +#[kani::unwind(4)] +// 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..fc7a2a3c060c --- /dev/null +++ b/index.html @@ -0,0 +1,194 @@ + + + + + + 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.

+

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..d328f8806cf7 --- /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..b1bc1ee226a5 --- /dev/null +++ b/install-guide.html @@ -0,0 +1,238 @@ + + + + + + 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.
  • +
  • ctags is required for Kani's --visualize option to work correctly. Universal ctags is recommended.
  • +
+

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..bac7dcb1e5c3 --- /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..3e73bb58dfce --- /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..38c6690a1da9 --- /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..db859579b48b --- /dev/null +++ b/print.html @@ -0,0 +1,3814 @@ + + + + + + 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.

+

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.
  • +
  • ctags is required for Kani's --visualize option to work correctly. Universal ctags is recommended.
  • +
+

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. CBMC Viewer (latest release)
  6. +
  7. Kissat (Release 3.1.1)
  8. +
+

Kani has been tested in Ubuntu and macOS platforms.

+

Install dependencies on Ubuntu

+

Support is available for Ubuntu 18.04, 20.04 and 22.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:

+
cargo build-dev
+
+

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 debugging verification failures section.

    +
  • +
  • +

    --visualize: Experimental, --enable-unstable feature that generates an HTML report providing traces (i.e., counterexamples) for each failure found by Kani.

    +
  • +
  • +

    --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 two important things:

+
    +
  1. It sets cfg(kani).
  2. +
  3. It injects the kani crate.
  4. +
+

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 debugging verification failures +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
+
+

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.

+

Getting a trace

+

By default, Kani only reports failures, not how the failure happened. +In this running example, it seems obvious what we're interested in (the value of x that caused the failure) because we just have one unknown input at the start (similar to the property test), but that's kind of a special case. +In general, understanding how a failure happened requires exploring a full (potentially large) execution trace.

+

An execution trace is a record of exactly how a failure can occur. +Nondeterminism (like a call to kani::any(), which could return any value) can appear in the middle of its execution. +A trace is a record of exactly how execution proceeded, including concrete choices (like 1023) for all of these nondeterministic values.

+

To get a trace for a failing check in Kani, run:

+
cargo kani --visualize --enable-unstable
+
+

This command runs Kani and generates an HTML report that includes a trace. +Open the report with your preferred browser. +Under the "Errors" heading, click on the "trace" link to find the trace for this failure.

+

From this trace report, we can filter through it to find relevant lines. +A good rule of thumb is to search for either kani::any() or assignments to variables you're interested in. +At present time, an unfortunate amount of generated code is present in the trace. +This code isn't a part of the Rust code you wrote, but is an internal implementation detail of how Kani runs proof harnesses. +Still, searching for kani::any() quickly finds us these lines:

+
let x: u32 = kani::any();
+x = 1023u
+
+

Here we're seeing the line of code and the value assigned in this particular trace. +Like property testing, this is just one example of a failure. +To proceed, we recommend fixing the code to avoid this particular issue and then re-running Kani to see if you find more issues.

+

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);
+}
+
+

But now 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. +Try running:

+
cargo kani --visualize --harness verify_success
+
+

The beginning of the report includes coverage information. +Clicking through to the file will show fully-covered lines in green. +Lines not covered by our proof harness will show in red.

+

Try changing the assumption in the proof harness to x < 2048. +Now the harness won't be testing all possible cases. +Rerun cargo kani --visualize. +Look at the report: you'll see we no longer have 100% coverage of the function.

+

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 to get a failing trace using kani --visualize
  8. +
  9. We saw how proof harnesses are used to set up preconditions with kani::assume().
  10. +
  11. We saw how to obtain coverage metrics and use them to ensure our proofs are covering as much as they should be.
  12. +
+

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.get_unchecked(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, 351.) +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 351 failed
+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: Remember how to get a trace from Kani? Find out what inputs it failed on.
  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 two failures:

+
SUMMARY: 
+ ** 2 of 350 failed
+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
+Failed Checks: dereference failure: pointer outside object bounds
+ File: "./src/bounds_check.rs", line 11, in bounds_check::get_wrapped
+
+VERIFICATION:- FAILED
+
+

The first is Rust's runtime bounds checking for the safe indexing operation. +The second is Kani's check to ensure the pointer operation is actually safe. +This pattern (two checks for similar issues in safe Rust code) is common to see, and we'll see it again in the next section.

+
+

NOTE: While Kani will always be checking for both properties, in the future the output here may change. +You might have noticed that the bad pointer dereference can't happen, since the bounds check would panic first. +In the future, Kani's output may report only the bounds checking failure in this example.

+
+
+
+Click to see explanation for exercise 2 +

Having run cargo kani --harness bound_check --visualize and clicked on one of the failures to see a trace, there are three things to immediately notice:

+
    +
  1. This trace is huge. Because the standard library Vec is involved, there's a lot going on.
  2. +
  3. The top of the trace file contains some "trace navigation tips" that might be helpful in navigating the trace.
  4. +
  5. There's a lot of generated code and it's really hard to just read the trace itself.
  6. +
+

To navigate this trace to find the information you need, we again recommend searching for things you expect to be somewhere in the trace:

+
    +
  1. Search the page for kani::any or <variable_of_interest> = such as size = or let size. +We can use this to find out what example values lead to a problem. +In this case, where we just have a couple of kani::any values in our proof harness, we can learn a lot just by seeing what these are. +In this trace we find (and the values you get may be different):
  2. +
+
Step 36: Function bound_check, File src/bounds_check.rs, Line 37
+let size: usize = kani::any();
+size = 2464ul
+
+Step 39: Function bound_check, File src/bounds_check.rs, Line 39
+let index: usize = kani::any();
+index = 2463ul
+
+

You may see different values here, as it depends on the solver's behavior.

+
    +
  1. Try searching for failure:. This will be near the end of the page. +You can now search upwards from a failure to see what values certain variables had. +Sometimes it can be helpful to change the source code to add intermediate variables, so their value is visible in the trace. +For instance, you might want to compute the index before indexing into the array. +That way you'd see in the trace exactly what value is being used.
  2. +
+

These two techniques should help you find both the nondeterministic inputs, and the values that were involved in the failing assertion.

+
+

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 got more experience reading the traces that Kani generates, to debug a failing proof harness.
  6. +
  7. We saw Kani spot a division by zero error and an overflowing addition.
  8. +
  9. As an exercise, we tried proving an assertion (finding the midpoint) that was not completely trivial.
  10. +
+

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. +
+

Debugging verification failures

+

When the result of a certain check comes back as a FAILURE, +Kani offers different options to help debug:

+
    +
  • --concrete-playback. This experimental feature generates a Rust unit test case that plays back a failing +proof harness using a concrete counterexample.
  • +
  • --visualize. This feature generates an HTML text-based trace that +enumerates the execution steps leading to the check failure.
  • +
+

Concrete playback

+

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.
  • +
+

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 order 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.

+

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.

+

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 --visualize. 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.

+

CLion / IntelliJ

+

This is not a great solution, but it works for now (see https://github.com/intellij-rust/intellij-rust/issues/1618 +for more details). +Edit the Cargo.toml of the package that you're working on and add artificial dependencies on the rustc packages that you would like to explore.

+
# This configuration doesn't exist so it shouldn't affect your build.
+[target.'cfg(KANI_DEV)'.dependencies]
+# Adjust the path here to point to a local copy of the rust compiler.
+# The best way is to use the rustup path. Replace <toolchain> with the
+# proper name to your toolchain.
+rustc_driver = { path = "~/.rustup/toolchains/<toolchain>/lib/rustlib/rustc-src/rust/compiler/rustc_driver" }
+rustc_interface = { path = "~/.rustup/toolchains/<toolchain>/lib/rustlib/rustc-src/rust/compiler/rustc_interface" }
+
+

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_uncheckedNo
floorf32Yes
floorf64Yes
fmaf32No
fmaf64No
fmul_fastPartial#809
forgetYes
frem_fastNo
fsub_fastYes
likelyYes
log10f32No
log10f64No
log2f32No
log2f64No
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
powif32No
powif64No
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
sqrtf32No
sqrtf64No
sub_with_overflowYes
transmutePartialDoesn't check all UB conditions
truncf32Yes
truncf64Yes
tryNo#267
type_idYes
type_nameYes
typed_swapYes
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
+

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.
  • +
+

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..7a3511571ff8 --- /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..ad2d688a3136 --- /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 order 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/stubbing.html b/reference/stubbing.html new file mode 100644 index 000000000000..af7949b21c78 --- /dev/null +++ b/reference/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..c353051472ee --- /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..985ae32efe4f --- /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..641d8b58095a --- /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..60117563dd2d --- /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..667daa216e20 --- /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

+

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?
  • +
+

We recommend you to leave this empty for the first version of your RFC.

+

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 <= 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 a special variable called result (see open +questions on a discussion about (re)naming). 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 = my_div(dividend, divisor);
    +  kani::assert(result <= dividend); // 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(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({ let x = self.is_empty(); old(x) } || result.is_some())]
+Valid ✅: #[kani::ensures(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(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 = Self::pop(self);
+    kani::assert(self.is_empty() || self.len() == old_1 - 1)
+  }
+}
+
+

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(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

+ + + +

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/0001-mir-linker.html b/rfc/rfcs/0001-mir-linker.html new file mode 100644 index 000000000000..2aa65807d6a0 --- /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..8f02b7912304 --- /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..fa3504888484 --- /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..24c057078d96 --- /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..97bbb91f4f1a --- /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..338adfe74069 --- /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..8593b93d7d95 --- /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..86daa9cf6446 --- /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..665d2cadd034 --- /dev/null +++ b/rfc/rfcs/0009-function-contracts.html @@ -0,0 +1,969 @@ + + + + + + 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 <= 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 a special variable called result (see open +questions on a discussion about (re)naming). 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 = my_div(dividend, divisor);
    +  kani::assert(result <= dividend); // 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(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({ let x = self.is_empty(); old(x) } || result.is_some())]
+Valid ✅: #[kani::ensures(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(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 = Self::pop(self);
+    kani::assert(self.is_empty() || self.len() == old_1 - 1)
+  }
+}
+
+

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(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

+ + + +

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/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..b9ad2bb7cda3 --- /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"],"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":152,"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":257,"breadcrumbs":6,"title":3},"122":{"body":105,"breadcrumbs":4,"title":1},"123":{"body":113,"breadcrumbs":5,"title":2},"124":{"body":0,"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},"14":{"body":85,"breadcrumbs":5,"title":2},"15":{"body":146,"breadcrumbs":7,"title":4},"16":{"body":46,"breadcrumbs":8,"title":5},"17":{"body":8,"breadcrumbs":5,"title":2},"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":50,"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: Unstable 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