diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Binaries/boogie boogie-2.4.1+dfsg/Binaries/boogie --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Binaries/boogie 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Binaries/boogie 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# Wrapper script to run Boogie.exe on non-Windows systems using Mono +# +# Adapted from https://github.com/Microsoft/dafny/blob/master/Binaries/dafny + +MONO=$(which mono) + +# find the source directory for this script even if it's been symlinked +# from https://stackoverflow.com/questions/59895/ +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" +done +BOOGIE_ROOT="$( cd -P "$( dirname "$SOURCE" )" && pwd )" +BOOGIE="$BOOGIE_ROOT/Boogie.exe" + +if [[ ! -x "$MONO" ]]; then + echo "Error: Boogie requires Mono to run on non-Windows systems." + exit 1 +fi + +if [[ ! -e "$BOOGIE" ]]; then + echo "Error: Boogie.exe not found at $BOOGIE." + exit 1 +fi + +"$MONO" "$BOOGIE" "$@" diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Binaries/Boogie.vshost.exe.manifest boogie-2.4.1+dfsg/Binaries/Boogie.vshost.exe.manifest --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Binaries/Boogie.vshost.exe.manifest 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Binaries/Boogie.vshost.exe.manifest 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ - - - - - - - - - - - diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/boogie.1 boogie-2.4.1+dfsg/debian/boogie.1 --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/boogie.1 2016-10-30 01:52:14.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/boogie.1 2019-12-16 13:25:20.000000000 +0000 @@ -1,16 +1,21 @@ -.\" © 2013, 2015-2016 Benjamin Barenblat +.\" © 2013, 2015-2016 Benjamin Barenblat. Licensed under the Expat license: .\" -.\" 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. +.\" 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. .pc .TH boogie 1 "2016-08-17" "Git snapshot 1f2d6c1" Boogie .SH NAME @@ -755,6 +760,6 @@ .SH SEE ALSO .BR dot (1) .SH COPYRIGHT -Boogie is copyright \(co 2003-2015 Microsoft Corporation and licensed under the Microsoft Public License . +Boogie is copyright \(co 2003-2015 Microsoft Corporation and licensed under the Expat license. -This manual page is copyright \(co 2013, 2015-2016 Benjamin Barenblat and licensed under the Apache License, Version 2.0. +This manual page is copyright \(co 2013, 2015-2016 Benjamin Barenblat and licensed under the Expat license. diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/bvd.1 boogie-2.4.1+dfsg/debian/bvd.1 --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/bvd.1 2016-10-30 01:52:14.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/bvd.1 2019-12-16 13:25:20.000000000 +0000 @@ -1,16 +1,21 @@ -.\" © 2013, 2015-2016 Benjamin Barenblat +.\" © 2013, 2015-2016 Benjamin Barenblat. Licensed under the Expat license: .\" -.\" 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, W.TPOUT -.\" WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -.\" License for the specific language governing permissions and limitations -.\" under the License. +.\" 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. .pc .TH bvd 1 "2016-08-17" "Git snapshot 1f2d6c1" Boogie .SH NAME @@ -24,6 +29,6 @@ .B bvd is a graphical program and accepts no options on the command line. .SH COPYRIGHT -Boogie is copyright \(co 2003-2015 Microsoft Corporation and licensed under the Microsoft Public License . +Boogie is copyright \(co 2003-2015 Microsoft Corporation and licensed under the Expat license. -This manual page is copyright \(co 2013, 2015-2016 Benjamin Barenblat and licensed under the Apache License, Version 2.0. +This manual page is copyright \(co 2013, 2015-2016 Benjamin Barenblat and licensed under the Expat license. diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/changelog boogie-2.4.1+dfsg/debian/changelog --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/changelog 2016-10-30 01:52:24.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/changelog 2019-12-16 13:25:20.000000000 +0000 @@ -1,3 +1,24 @@ +boogie (2.4.1+dfsg-0.1) unstable; urgency=medium + + * Non-maintainer upload. + * New upstream release. + * Update debian/watch file. + * Update debian/copyright: + - New upstream license. + - Relicense debian/* to match new upstream license (Expat) + (see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=944876#20). + - Exclude prebuilt Windows binary from the source tree. + * Update licenses in debian/{boogie,bvd}.1. + * Change Priority to optional in debian/control. + * Upgrade to debhelper compat level 12. + * Update build dependencies (Closes: #927171). + * Upgrade to Standards-Version 4.4.1. + * Fix debian/rules to make the new version build. + * Enable autopkgtest package testing, and add mccarthy-{91,92} tests. + * Update Vcs-Git and Vcs-Browser fields in debian/control. + + -- Fabian Wolff Mon, 16 Dec 2019 14:25:20 +0100 + boogie (2.3.0.61016+dfsg+3.gbp1f2d6c1-1) unstable; urgency=medium ** SNAPSHOT build @1f2d6c15cc587e9e1b91be70186ee9a26d5e1928 ** diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/compat boogie-2.4.1+dfsg/debian/compat --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/compat 2016-10-29 21:40:41.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -9 diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/control boogie-2.4.1+dfsg/debian/control --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/control 2016-10-30 01:52:15.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/control 2019-12-08 06:34:31.000000000 +0000 @@ -1,18 +1,15 @@ Source: boogie Section: cli-mono -Priority: extra +Priority: optional Maintainer: Benjamin Barenblat -Build-Depends: - debhelper (>= 9), -Build-Depends-Indep: - cli-common-dev (>= 0.8), - mono-devel (>= 2.4.2.3), - mono-reference-assemblies-4.0, - tzdata, -Standards-Version: 3.9.8 +Build-Depends: debhelper-compat (= 12), + cli-common-dev, + mono-devel, + tzdata +Standards-Version: 4.4.1 Homepage: http://research.microsoft.com/en-us/projects/boogie/ -Vcs-Browser: https://benjamin.barenblat.name/gitweb/?p=debian-boogie.git -Vcs-Git: git://benjamin.barenblat.name/debian-boogie.git +Vcs-Browser: https://salsa.debian.org/debian/boogie +Vcs-Git: https://salsa.debian.org/debian/boogie.git Package: boogie Architecture: all diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/copyright boogie-2.4.1+dfsg/debian/copyright --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/copyright 2016-10-29 21:40:41.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/copyright 2019-12-16 13:25:20.000000000 +0000 @@ -1,91 +1,34 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Boogie -Upstream-Contact: boogie-dev@imperial.ac.uk Source: https://github.com/boogie-org/boogie -Copyright: 2003-2014 Microsoft -License: Ms-PL +Files-Excluded: Util/VS2010/Boogie/BoogieLanguageService/Resources/Irony.dll +Comment: Irony.dll is a prebuilt Windows binary whose source code is not + included in this package, so it does not belong into the Debian + package. -Files: * +Files: * Copyright: 2009-2015 Microsoft Corporation -License: Ms-PL +License: Expat -Files: debian/* -Copyright: 2015-2016 Benjamin Barenblat -License: Apache-2.0 +Files: debian/* +Copyright: 2019 Fabian Wolff + 2015-2016 Benjamin Barenblat +License: Expat -License: Apache-2.0 - 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. - . - On Debian systems, the complete text of the Apache License, Version 2.0, can be - found in "/usr/share/common-licenses/Apache-2.0". - -License: Ms-PL - Microsoft Public License (Ms-PL) - . - This license governs use of the accompanying software. If you use the software, - you accept this license. If you do not accept the license, do not use the - software. - . - 1. Definitions - . - The terms "reproduce," "reproduction," "derivative works," and "distribution" - have the same meaning here as under U.S. copyright law. - . - A "contribution" is the original software, or any additions or changes to the - software. - . - A "contributor" is any person that distributes its contribution under this - license. - . - "Licensed patents" are a contributor's patent claims that read directly on its - contribution. - . - 2. Grant of Rights - . - (A) Copyright Grant- Subject to the terms of this license, including the license - conditions and limitations in section 3, each contributor grants you a - non-exclusive, worldwide, royalty-free copyright license to reproduce its - contribution, prepare derivative works of its contribution, and distribute its - contribution or any derivative works that you create. - . - (B) Patent Grant- Subject to the terms of this license, including the license - conditions and limitations in section 3, each contributor grants you a - non-exclusive, worldwide, royalty-free license under its licensed patents to - make, have made, use, sell, offer for sale, import, and/or otherwise dispose of - its contribution in the software or derivative works of the contribution in the - software. - . - 3. Conditions and Limitations - . - (A) No Trademark License- This license does not grant you rights to use any - contributors' name, logo, or trademarks. - . - (B) If you bring a patent claim against any contributor over patents that you - claim are infringed by the software, your patent license from such contributor - to the software ends automatically. - . - (C) If you distribute any portion of the software, you must retain all - copyright, patent, trademark, and attribution notices that are present in the - software. - . - (D) If you distribute any portion of the software in source code form, you may - do so only under this license by including a complete copy of this license with - your distribution. If you distribute any portion of the software in compiled or - object code form, you may only do so under a license that complies with this - license. - . - (E) The software is licensed "as-is." You bear the risk of using it. The - contributors give no express warranties, guarantees or conditions. You may have - additional consumer rights under your local laws which this license cannot - change. To the extent permitted under your local laws, the contributors exclude - the implied warranties of merchantability, fitness for a particular purpose and - non-infringement. +License: Expat + 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 -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/gbp.conf boogie-2.4.1+dfsg/debian/gbp.conf --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/gbp.conf 2016-10-30 00:40:30.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/gbp.conf 2019-12-08 06:34:31.000000000 +0000 @@ -1,2 +1,4 @@ [DEFAULT] -upstream-tree = 1f2d6c15cc587e9e1b91be70186ee9a26d5e1928 +debian-branch = master +upstream-branch = upstream +pristine-tar = True diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/rules boogie-2.4.1+dfsg/debian/rules --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/rules 2016-10-30 01:51:01.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/rules 2019-12-08 06:34:31.000000000 +0000 @@ -1,9 +1,18 @@ #!/usr/bin/make -f -# -*- makefile -*- + +# Uncomment this to turn on verbose mode +# export DH_VERBOSE = 1 %: dh $@ --with cli -.PHONY: override_dh_auto_build override_dh_auto_build: + # Apparently, the Mono C# compiler does not support pattern + # matching yet. The proper thing to do here would be to patch + # Witnesses.cs, but unfortunately, dpkg-source fails + # spectacularly when it comes to patching files with DOS + # (CRLF) line endings. Therefore, we need to do a "manual + # patch" here: + sed -i 's/is null/== null/g' Source/Concurrency/Witnesses.cs xbuild /p:TargetFrameworkVersion=v4.0 Source/Boogie.sln + sed -i 's/is null/== null/g' Source/Concurrency/Witnesses.cs diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/tests/control boogie-2.4.1+dfsg/debian/tests/control --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/tests/control 2019-12-08 06:34:31.000000000 +0000 @@ -0,0 +1,2 @@ +Tests: mccarthy-91 mccarthy-92 +Depends: @ diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/tests/mccarthy-91 boogie-2.4.1+dfsg/debian/tests/mccarthy-91 --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/tests/mccarthy-91 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/tests/mccarthy-91 2019-12-08 06:34:31.000000000 +0000 @@ -0,0 +1,31 @@ +#!/bin/sh + +set -e + +# Run an example stolen from https://rise4fun.com/Boogie/McCarthy-91 + +TMPDIR=$(mktemp -d) +trap "rm -rf $TMPDIR" EXIT +cd $TMPDIR + +cat < test.bpl +procedure F(n: int) returns (r: int) + ensures 100 < n ==> r == n - 10; // This postcondition is easy to check by hand + ensures n <= 100 ==> r == 91; // Do you believe this one is true? +{ + if (100 < n) { + r := n - 10; + } else { + call r := F(n + 11); + call r := F(r); + } +} +EOF + +cat < expected +Boogie program verifier finished with 1 verified, 0 errors +EOF + +boogie test.bpl > boogie_output +tail -n1 boogie_output > result +diff result expected diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/tests/mccarthy-92 boogie-2.4.1+dfsg/debian/tests/mccarthy-92 --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/tests/mccarthy-92 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/tests/mccarthy-92 2019-12-08 06:34:31.000000000 +0000 @@ -0,0 +1,32 @@ +#!/bin/sh + +set -e + +# Run an example based on https://rise4fun.com/Boogie/McCarthy-91 +# This example is adapted to be wrong: We should not be able to verify it + +TMPDIR=$(mktemp -d) +trap "rm -rf $TMPDIR" EXIT +cd $TMPDIR + +cat < test.bpl +procedure F(n: int) returns (r: int) + ensures 100 < n ==> r == n - 10; + ensures n <= 100 ==> r == 92; // This would have to be 91 for the program to verify +{ + if (100 < n) { + r := n - 10; + } else { + call r := F(n + 11); + call r := F(r); + } +} +EOF + +cat < expected +Boogie program verifier finished with 0 verified, 1 error +EOF + +boogie test.bpl > boogie_output +tail -n1 boogie_output > result +diff result expected diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/watch boogie-2.4.1+dfsg/debian/watch --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/debian/watch 2016-10-29 21:40:41.000000000 +0000 +++ boogie-2.4.1+dfsg/debian/watch 2019-12-16 13:25:20.000000000 +0000 @@ -1,4 +1,6 @@ -# Upstream does not cut releases at this point – only Git snapshots. -# Consequently, constructing a suitable watch file is impossible. -# -# -- Benjamin Barenblat Tue 11 Aug 2015 19:27:24 -0400 +version=4 +opts="repacksuffix=+dfsg,repack, \ + dversionmangle=auto, \ + filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%boogie-$1.tar.gz%" \ + https://github.com/boogie-org/boogie/releases \ + (?:.*?/)?v?(\d[\d.]*)\.tar\.gz diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/.gitignore boogie-2.4.1+dfsg/.gitignore --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/.gitignore 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/.gitignore 2019-10-25 18:17:05.000000000 +0000 @@ -1,11 +1,12 @@ # Nuget Source/packages/* +Source/**/nupkg # Tests Source/UnitTests/TestResult.xml Source/UnitTests/*/bin Source/UnitTests/*/obj -Test/*/Output +Test/**/Output TestResult.xml # Binaries @@ -18,6 +19,7 @@ Binaries/*.exe Binaries/*.pdb Binaries/*.config +Binaries/*.vshost.exe.manifest # Editor temporary files .*.swp @@ -29,3 +31,12 @@ Source/*.user Source/*.suo Source/*.cache +Source/.vs/ + +.vscode + +# MonoDevelop files +Source/*.userprefs + +# Rider files +.idea/ diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/LICENSE.txt boogie-2.4.1+dfsg/LICENSE.txt --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/LICENSE.txt 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/LICENSE.txt 2019-10-25 18:17:05.000000000 +0000 @@ -1,60 +1,22 @@ -Microsoft Public License (Ms-PL) +Copyright (c) Microsoft Corporation -This license governs use of the accompanying software. If you use the software, -you accept this license. If you do not accept the license, do not use the -software. +All rights reserved. -1. Definitions +MIT License -The terms "reproduce," "reproduction," "derivative works," and "distribution" -have the same meaning here as under U.S. copyright law. - -A "contribution" is the original software, or any additions or changes to the -software. - -A "contributor" is any person that distributes its contribution under this -license. - -"Licensed patents" are a contributor's patent claims that read directly on its -contribution. - -2. Grant of Rights - -(A) Copyright Grant- Subject to the terms of this license, including the license -conditions and limitations in section 3, each contributor grants you a -non-exclusive, worldwide, royalty-free copyright license to reproduce its -contribution, prepare derivative works of its contribution, and distribute its -contribution or any derivative works that you create. - -(B) Patent Grant- Subject to the terms of this license, including the license -conditions and limitations in section 3, each contributor grants you a -non-exclusive, worldwide, royalty-free license under its licensed patents to -make, have made, use, sell, offer for sale, import, and/or otherwise dispose of -its contribution in the software or derivative works of the contribution in the -software. - -3. Conditions and Limitations - -(A) No Trademark License- This license does not grant you rights to use any -contributors' name, logo, or trademarks. - -(B) If you bring a patent claim against any contributor over patents that you -claim are infringed by the software, your patent license from such contributor -to the software ends automatically. - -(C) If you distribute any portion of the software, you must retain all -copyright, patent, trademark, and attribution notices that are present in the -software. - -(D) If you distribute any portion of the software in source code form, you may -do so only under this license by including a complete copy of this license with -your distribution. If you distribute any portion of the software in compiled or -object code form, you may only do so under a license that complies with this -license. - -(E) The software is licensed "as-is." You bear the risk of using it. The -contributors give no express warranties, guarantees or conditions. You may have -additional consumer rights under your local laws which this license cannot -change. To the extent permitted under your local laws, the contributors exclude -the implied warranties of merchantability, fitness for a particular purpose and -non-infringement. +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 -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/README.md boogie-2.4.1+dfsg/README.md --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/README.md 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/README.md 2019-10-25 18:17:05.000000000 +0000 @@ -52,7 +52,7 @@ ### Requirements - [NuGet](https://www.nuget.org/) -- [Z3](https://github.com/Z3Prover/z3) 4.4.1 or [CVC4](http://cvc4.cs.nyu.edu/web/) **FIXME_VERSION** (note +- [Z3](https://github.com/Z3Prover/z3) 4.8.4 (earlier versions may also work, but the test suite assumes 4.8.4 to produce the expected output) or [CVC4](http://cvc4.cs.nyu.edu/web/) **FIXME_VERSION** (note CVC4 support is experimental) #### Windows specific @@ -108,7 +108,19 @@ $ ln -s /usr/bin/z3 Binaries/z3.exe ``` -You're now ready to run Boogie! +## Running + +On a Windows system call the Boogie binary: + +``` +$ Binaries\Boogie.exe +``` + +On a non-Windows system use the wrapper script: + +``` +$ Binaries/boogie +``` ## Testing diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/AbsInt/AbsInt.csproj boogie-2.4.1+dfsg/Source/AbsInt/AbsInt.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/AbsInt/AbsInt.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/AbsInt/AbsInt.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,7 +10,7 @@ Properties AbsInt BoogieAbsInt - v4.0 + v4.5 512 1 true @@ -34,7 +34,7 @@ false false true - Client + true @@ -74,6 +74,7 @@ Full %28none%29 AllRules.ruleset + false pdbonly @@ -83,6 +84,7 @@ prompt 4 AllRules.ruleset + false true @@ -99,6 +101,7 @@ true 4 false + false true @@ -144,6 +147,7 @@ 0 4 false + false true @@ -162,6 +166,7 @@ true 4 false + false bin\x86\Release\ @@ -179,6 +184,7 @@ ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true 4 + false true @@ -196,6 +202,7 @@ true 4 false + false true @@ -212,6 +219,7 @@ ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false + false true @@ -221,6 +229,7 @@ AnyCPU prompt AllRules.ruleset + false true @@ -230,6 +239,7 @@ AnyCPU prompt AllRules.ruleset + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/AbsInt/AbsInt-NetCore.csproj boogie-2.4.1+dfsg/Source/AbsInt/AbsInt-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/AbsInt/AbsInt-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/AbsInt/AbsInt-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,30 @@ + + + + Library + BoogieAbsInt + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + + + + false + + + + + version.cs + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/AIFramework/AIFramework.csproj boogie-2.4.1+dfsg/Source/AIFramework/AIFramework.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/AIFramework/AIFramework.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/AIFramework/AIFramework.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,7 +10,7 @@ Properties AIFramework AIFramework - v4.0 + v4.5 512 1 true @@ -34,7 +34,7 @@ false false true - Client + true @@ -74,6 +74,7 @@ Full %28none%29 AllRules.ruleset + false pdbonly @@ -83,6 +84,7 @@ prompt 4 AllRules.ruleset + false true @@ -97,6 +99,7 @@ prompt Migrated rules for AIFramework.ruleset true + false true @@ -137,6 +140,7 @@ Full Build 0 + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/AIFramework/AIFramework-NetCore.csproj boogie-2.4.1+dfsg/Source/AIFramework/AIFramework-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/AIFramework/AIFramework-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/AIFramework/AIFramework-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,26 @@ + + + + Library + AIFramework + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + + false + + + + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Basetypes/Basetypes.csproj boogie-2.4.1+dfsg/Source/Basetypes/Basetypes.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Basetypes/Basetypes.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Basetypes/Basetypes.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,7 +10,7 @@ Properties Basetypes BoogieBasetypes - v4.0 + v4.5 512 1 true @@ -34,7 +34,7 @@ false false true - Client + true @@ -74,6 +74,7 @@ Full %28none%29 AllRules.ruleset + false pdbonly @@ -83,6 +84,7 @@ prompt 4 AllRules.ruleset + false true @@ -99,6 +101,7 @@ true 4 false + false true @@ -141,6 +144,7 @@ 0 4 false + false true @@ -150,6 +154,7 @@ AnyCPU prompt AllRules.ruleset + false @@ -162,6 +167,7 @@ version.cs + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Basetypes/Basetypes-NetCore.csproj boogie-2.4.1+dfsg/Source/Basetypes/Basetypes-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Basetypes/Basetypes-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Basetypes/Basetypes-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,27 @@ + + + + Library + BoogieBasetypes + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + false + + + + + version.cs + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Basetypes/BigFloat.cs boogie-2.4.1+dfsg/Source/Basetypes/BigFloat.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Basetypes/BigFloat.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Basetypes/BigFloat.cs 2019-10-25 18:17:05.000000000 +0000 @@ -5,49 +5,29 @@ //----------------------------------------------------------------------------- using System; -using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Linq; using System.Text; -using System.Diagnostics.Contracts; -using System.Diagnostics; namespace Microsoft.Basetypes { using BIM = System.Numerics.BigInteger; - + /// - /// A representation of a 32-bit floating point value - /// Note that this value has a 1-bit sign, 8-bit exponent, and 24-bit significand + /// A representation of a floating-point value + /// Note that this value has a 1-bit sign, along with an exponent and significand whose sizes must be greater than 1 /// public struct BigFloat { //Please note that this code outline is copy-pasted from BigDec.cs // the internal representation - [Rep] internal readonly BIM significand; //Note that the significand arrangement matches standard fp arrangement (most significant bit is farthest left) - [Rep] - internal readonly int significandSize; - [Rep] + internal readonly int significandSize; //The bit size of the significand internal readonly BIM exponent; //The value of the exponent is always positive as per fp representation requirements - [Rep] internal readonly int exponentSize; //The bit size of the exponent - [Rep] internal readonly String value; //Only used with second syntax - [Rep] - internal readonly bool isNeg; - - public BIM Significand { - get { - return significand; - } - } - - public BIM Exponent { - get { - return exponent; - } - } + internal readonly bool isSignBitSet; //Stores the sign bit in the fp representaiton public int SignificandSize { get { @@ -61,30 +41,8 @@ } } - public bool IsNegative { - get { - return this.isNeg; - } - } - - public String Value { - get { - return value; - } - } - public static BigFloat ZERO = new BigFloat(false, BIM.Zero, BIM.Zero, 24, 8); //Does not include negative zero - private static readonly BIM two = new BIM(2); - private static readonly BIM one = new BIM(1); - private static BIM two_n(int n) { - BIM toReturn = one; - for (int i = 0; i < n; i++) - toReturn = toReturn * two; - return toReturn; - } - - //////////////////////////////////////////////////////////////////////////// // Constructors @@ -96,79 +54,108 @@ return new BigFloat(v.ToString(), 24, 8); } - public static BigFloat FromInt(int v, int significandSize, int exponentSize) - { + public static BigFloat FromBigInt(BIM v, int significandSize, int exponentSize) { return new BigFloat(v.ToString(), significandSize, exponentSize); } - public static BigFloat FromBigInt(BIM v) { - return new BigFloat(v.ToString(), 24, 8); - } + [Pure] + public static BigFloat FromString(String s) { + /* + * String must be either of the format [-]0x^.^e*f*e* + * or of the special value formats: 0NaN*e* 0nan*e* 0+oo*e* 0-oo*e* + * Where ^ indicates a hexadecimal value and * indicates an integer value + */ - public static BigFloat FromBigInt(BIM v, int significandSize, int exponentSize) - { - return new BigFloat(v.ToString(), significandSize, exponentSize); - } + int posLastE = s.LastIndexOf('e'); - public static BigFloat FromBigDec(BigDec v) - { - return new BigFloat(v.ToDecimalString(), 24, 8); - } + int expSize = int.Parse(s.Substring(posLastE + 1)); + if (expSize <= 1) { + throw new FormatException("Exponent size must be greater than 1"); + } - public static BigFloat FromBigDec(BigDec v, int significandSize, int exponentSize) - { - return new BigFloat(v.ToDecimalString(), significandSize, exponentSize); - } + int posLastF = s.LastIndexOf('f'); + int posSig = posLastF + 1; + if (posLastF == -1) {//NaN, +oo, -oo + posSig = 4; + } - [Pure] - public static BigFloat FromString(String s) { - /* - * String must be either of the format *e*f*e* - * or of the special value formats: 0NaN*e* 0nan*e* 0+oo*e* 0-oo*e* - * Where * indicates an integer value (digit) - */ - BIM sig, exp; - int sigSize, expSize; - bool isNeg; - - if (s.IndexOf('f') == -1) { - String val = s; - sigSize = int.Parse(s.Substring(4, s.IndexOf('e')-4)); - expSize = int.Parse(s.Substring(s.IndexOf('e') + 1)); - if (sigSize <= 0 || expSize <= 0) - throw new FormatException("Significand and Exponent sizes must be greater than 0"); - return new BigFloat(val, sigSize, expSize); - } - - sig = BIM.Parse(s.Substring(0, s.IndexOf('e'))); - exp = BIM.Parse(s.Substring(s.IndexOf('e') + 1, s.IndexOf('f') - s.IndexOf('e') - 1)); - sigSize = int.Parse(s.Substring(s.IndexOf('f') + 1, s.IndexOf('e', s.IndexOf('e') + 1) - s.IndexOf('f') - 1)); - expSize = int.Parse(s.Substring(s.IndexOf('e', s.IndexOf('e') + 1) + 1)); - isNeg = s[0] == '-'; //We do this so that -0 is parsed correctly - - if (sigSize <= 0 || expSize <= 0) - throw new FormatException("Significand and Exponent sizes must be greater than 0"); - - sigSize = sigSize - 1; //Get rid of sign bit - sig = BIM.Abs(sig); //sig must be positive - //Uncomment if you want to shift the exponent for the user (i.e. 0e-1f24e8 --> 0e126f24e8) - //exp = exp + BIM.Pow(new BIM(2), expSize-1) - BIM.One; - - if (exp < 0 || exp >= BIM.Pow(new BIM(2), expSize)) - throw new FormatException("The given exponent " + exp + " cannot fit in the bit size " + expSize); + int sigSize = int.Parse(s.Substring(posSig, posLastE - posSig)); + if (sigSize <= 1) { + throw new FormatException("Significand size must be greater than 1"); + } + + if (posLastF == -1) {//NaN, +oo, -oo + return new BigFloat(s.Substring(1, 3), sigSize, expSize); + } + + bool isSignBitSet = s[0] == '-'; + + int posX = s.IndexOf('x'); + int posSecondLastE = s.LastIndexOf('e', posLastE - 1); + + string hexSig = s.Substring(posX + 1, posSecondLastE - (posX + 1)); + BIM oldExp = BIM.Parse(s.Substring(posSecondLastE + 1, posLastF - (posSecondLastE + 1))); + + string binSig = string.Join(string.Empty, + hexSig.Select( + c => (c == '.' ? "." : Convert.ToString(Convert.ToInt32(c.ToString(), 16), 2).PadLeft(4, '0')) + ) + ); + + int posDec = binSig.IndexOf('.'); + + binSig = binSig.Remove(posDec, 1); + + int posFirstOne = binSig.IndexOf('1'); + int posLastOne = binSig.LastIndexOf('1'); + + if (posFirstOne == -1) { + return new BigFloat(isSignBitSet, 0, 0, sigSize, expSize); + } + + binSig = binSig.Substring(posFirstOne, posLastOne - posFirstOne + 1); + + BIM bias = BIM.Pow(2, expSize - 1) - 1; + BIM upperBound = 2 * bias + 1; + + BIM newExp = 4 * oldExp + bias + (posDec - posFirstOne - 1); + + if (newExp <= 0) { + if (-newExp <= (sigSize - 1) - binSig.Length) { + binSig = new string('0', (int) -newExp) + binSig; + newExp = 0; + } + } else { + binSig = binSig.Substring(1); + } + + if (newExp < 0 || newExp >= upperBound) { + throw new FormatException("The given exponent cannot fit in the bit size " + expSize); + } - if (sig >= BIM.Pow(new BIM(2), sigSize)) - throw new FormatException("The given significand " + sig + " cannot fit in the bit size " + (sigSize+1)); + binSig = binSig.PadRight(sigSize - 1, '0'); - return new BigFloat(isNeg, sig, exp, sigSize, expSize); + if (binSig.Length > sigSize - 1) { + throw new FormatException("The given significand cannot fit in the bit size " + (sigSize - 1)); + } + + BIM newSig = 0; + foreach (char b in binSig) { + if (b != '.') { + newSig <<= 1; + newSig += b - '0'; + } + } + + return new BigFloat(isSignBitSet, newSig, newExp, sigSize, expSize); } - public BigFloat(bool isNeg, BIM significand, BIM exponent, int significandSize, int exponentSize) { + public BigFloat(bool isSignBitSet, BIM significand, BIM exponent, int significandSize, int exponentSize) { this.exponentSize = exponentSize; this.exponent = exponent; this.significand = significand; - this.significandSize = significandSize+1; - this.isNeg = isNeg; + this.significandSize = significandSize; + this.isSignBitSet = isSignBitSet; this.value = ""; } @@ -178,21 +165,12 @@ this.exponent = BIM.Zero; this.significand = BIM.Zero; this.value = value; - if (value.Equals("nan")) + if (value.Equals("nan")) { this.value = "NaN"; - this.isNeg = value[0] == '-'; - } + } - private BIM maxsignificand() - { - BIM result = one; - for (int i = 0; i < significandSize; i++) - result = result * two; - return result - one; + this.isSignBitSet = value[0] == '-'; } - private int maxExponent() { return (int)Math.Pow(2, exponentSize) - 1; } - - //////////////////////////////////////////////////////////////////////////// // Basic object operations @@ -200,309 +178,413 @@ [Pure] [Reads(ReadsAttribute.Reads.Nothing)] public override bool Equals(object obj) { - if (obj == null) + if (obj == null) { return false; - if (!(obj is BigFloat)) + } + + if (!(obj is BigFloat)) { return false; + } - return (this == (BigFloat)obj); + return (this == (BigFloat) obj); } [Pure] public override int GetHashCode() { - return significand.GetHashCode() * 13 + Exponent.GetHashCode(); + return significand.GetHashCode() * 13 + exponent.GetHashCode(); } [Pure] public override string/*!*/ ToString() { Contract.Ensures(Contract.Result() != null); - return value=="" ? String.Format("{0}x2^{1}", significand.ToString(), Exponent.ToString()) : value; - } - + if (value == "") { + byte[] sigBytes = significand.ToByteArray(); + StringBuilder binSig = new StringBuilder(); - //////////////////////////////////////////////////////////////////////////// - // Conversion operations - - /// - /// NOTE: THIS METHOD MAY NOT WORK AS EXPECTED!!! - /// Converts the given decimal value (provided as a string) to the nearest floating point approximation - /// the returned fp assumes the given significand and exponent size - /// - /// - /// - /// - /// - public static BigFloat Round(String value, int exponentSize, int significandSize) - { - int i = value.IndexOf('.'); - if (i == -1) - return Round(BIM.Parse(value), BIM.Zero, exponentSize, significandSize); - return Round(i == 0 ? BIM.Zero : BIM.Parse(value.Substring(0, i)), BIM.Parse(value.Substring(i + 1, value.Length - i - 1)), exponentSize, significandSize); - } + if (exponent == 0) { + binSig.Append('0'); + } else { + binSig.Append('1'); //hidden bit + } - /// - /// NOTE: THIS METHOD MAY NOT WORK AS EXPECTED!!!! - /// Converts value.dec_value to a the closest float approximation with the given significandSize, exponentSize - /// Returns the result of this calculation - /// - /// - /// - /// - /// - /// - public static BigFloat Round(BIM value, BIM dec_value, int exponentSize, int significandSize) - { - int exp = 0; - BIM one = new BIM(1); - BIM ten = new BIM(10); - BIM dec_max = new BIM(0); //represents the order of magnitude of dec_value for carrying during calculations - - //First, determine the exponent - while (value > one) { //Divide by two, increment exponent by 1 - if (!(value % two).IsZero) { //Add "1.0" to the decimal - dec_max = new BIM(10); - while (dec_max < dec_value) - dec_max = dec_max * ten; - dec_value = dec_value + dec_max; - } - value = value / two; - if (!(dec_value % ten).IsZero) - dec_value = dec_value * ten; //Creates excess zeroes to avoid losing data during division - dec_value = dec_value / two; - exp++; - } - if (value.IsZero && !dec_value.IsZero) { - dec_max = new BIM(10); - while (dec_max < dec_value) - dec_max = dec_max * ten; - while (value.IsZero) {//Multiply by two, decrement exponent by 1 - dec_value = dec_value * two; - if (dec_value >= dec_max) { - dec_value = dec_value - dec_max; - value = value + one; + for (int i = significandSize - 2; i >= 0; --i) { //little endian + if (i / 8 < sigBytes.Length) { + binSig.Append((char) ('0' + ((sigBytes[i / 8] >> (i % 8)) & 1))); + } else { + binSig.Append('0'); } - exp--; } - } - //Second, calculate the significand - value = new BIM(0); //remove implicit bit - dec_max = new BIM(10); - while (dec_max < dec_value) - dec_max = dec_max * ten; - for (int i = significandSize; i > 0 && !dec_value.IsZero; i--) { //Multiply by two until the significand is fully calculated - dec_value = dec_value * two; - if (dec_value >= dec_max) { - dec_value = dec_value - dec_max; - value = value + two_n(i); //Note that i is decrementing so that the most significant bit is left-most in the representation + BIM bias = BIM.Pow(2, exponentSize - 1) - 1; + if (exponent == 0) { + --bias; } - } - return new BigFloat(false, BIM.Zero, BIM.Parse(value.ToString()), exponentSize, significandSize); //Sign not actually checked... + int moveDec = ((int) ((exponent - bias) % 4) + 4) % 4; + BIM finalExp = (exponent - bias - moveDec) / 4; + + string leftBinSig = binSig.ToString().Substring(0, moveDec + 1); + string rightBinSig = binSig.ToString().Substring(moveDec + 1); + + leftBinSig = new string('0', 4 - leftBinSig.Length % 4) + leftBinSig; + rightBinSig = rightBinSig + new string('0', 4 - rightBinSig.Length % 4); + + StringBuilder leftHexSig = new StringBuilder(); + StringBuilder rightHexSig = new StringBuilder(); + + for (int i = 0; i < leftBinSig.Length / 4; ++i) { + leftHexSig.AppendFormat("{0:X}", Convert.ToByte(leftBinSig.Substring(i * 4, 4), 2)); + } + for (int i = 0; i < rightBinSig.Length / 4; ++i) { + rightHexSig.AppendFormat("{0:X}", Convert.ToByte(rightBinSig.Substring(i * 4, 4), 2)); + } + + return String.Format("{0}0x{1}.{2}e{3}f{4}e{5}", isSignBitSet ? "-" : "", leftHexSig, rightHexSig, finalExp, significandSize, exponentSize); + } + return String.Format("0{0}{1}e{2}", value, significandSize, exponentSize); } + + //////////////////////////////////////////////////////////////////////////// + // Conversion operations + // ``floor`` rounds towards negative infinity (like SMT-LIBv2's to_int). /// - /// NOTE: This may give wrong results, it hasn't been tested extensively - /// If you're getting weird bugs, you may want to check this function out... /// Computes the floor and ceiling of this BigFloat. Note the choice of rounding towards negative /// infinity rather than zero for floor is because SMT-LIBv2's to_int function floors this way. /// - /// The Floor (rounded towards negative infinity) + /// Floor (rounded towards negative infinity) /// Ceiling (rounded towards positive infinity) - public void FloorCeiling(out BIM floor, out BIM ceiling) - { - BIM two = new BIM(2); + public void FloorCeiling(out BIM floor, out BIM ceiling) { + Contract.Requires(value == ""); + + BIM sig = significand; + BIM exp = exponent; - BIM sig = Significand + BIM.Pow(two, SignificandSize); //Add hidden bit - BIM exp = Exponent - BIM.Pow(two, ExponentSize-1) + 1; + BIM hiddenBitPow = BIM.Pow(2, significandSize - 1); - while (exp > BIM.Zero) { - exp--; - sig = sig << 1; + if (exponent > 0) { + sig += hiddenBitPow; + } else { + ++exp; } - sig = sig >> SignificandSize; + exp -= (BIM.Pow(2, exponentSize - 1) - 1) + (significandSize - 1); + + if (exp >= BIM.Zero) { + while (exp >= int.MaxValue) { + sig <<= int.MaxValue; + exp -= int.MaxValue; + } + + sig <<= (int) exp; + floor = ceiling = (isSignBitSet ? -sig : sig); + } else { + exp = -exp; - if (isNeg) { - ceiling = -sig + 1; - floor = -sig; + if (exp > significandSize) { + if (sig == 0) { + floor = ceiling = 0; + } else { + ceiling = isSignBitSet ? 0 : 1; + floor = ceiling - 1; + } + } else { + BIM frac = sig & ((BIM.One << (int) exp) - 1); + sig >>= (int) exp; //Guaranteed to fit in a 32-bit integer + + if (frac == 0) { + floor = ceiling = (isSignBitSet ? -sig : sig); + } else { + ceiling = isSignBitSet ? -sig : sig + 1; + floor = ceiling - 1; + } + } } - else { - ceiling = sig + 1; - floor = sig; + } + + public String ToBVString() { + if (value != "") { + return "_ " + value + " " + exponentSize + " " + significandSize; + } else if (value == "") { + return "fp (_ bv" + (isSignBitSet ? "1" : "0") + " 1) (_ bv" + exponent + " " + exponentSize + ") (_ bv" + significand + " " + (significandSize - 1) + ")"; + } else { + return "(_ to_fp " + exponentSize + " " + significandSize + ") (_ bv" + value + " " + (exponentSize + significandSize).ToString() + ")"; } } + //////////////////////////////////////////////////////////////////////////// + // Basic arithmetic operations + [Pure] - public String ToDecimalString(int maxDigits) { - //TODO: fix for fp functionality - { - throw new NotImplementedException(); + public static BigFloat operator -(BigFloat x) { + if (x.value != "") { + if (x.value[0] == '-') { + return new BigFloat("+oo", x.significandSize, x.exponentSize); + } + + if (x.value[0] == '+') { + return new BigFloat("-oo", x.significandSize, x.exponentSize); + } + + return new BigFloat("NaN", x.significandSize, x.exponentSize); } + + return new BigFloat(!x.isSignBitSet, x.significand, x.exponent, x.significandSize, x.exponentSize); } - public String ToBVString(){ - if (this.IsSpecialType) { - return "_ " + this.value + " " + this.exponentSize + " " + this.significandSize; + [Pure] + public static BigFloat operator +(BigFloat x, BigFloat y) { + Contract.Requires(x.exponentSize == y.exponentSize); + Contract.Requires(x.significandSize == y.significandSize); + + if (x.value != "" || y.value != "") { + if (x.value == "NaN" || y.value == "NaN" || x.value == "+oo" && y.value == "-oo" || x.value == "-oo" && y.value == "+oo") { + return new BigFloat("NaN", x.significandSize, x.exponentSize); + } + + if (x.value != "") { + return new BigFloat(x.value, x.significandSize, x.exponentSize); + } + + return new BigFloat(y.value, y.significandSize, y.exponentSize); } - else if (this.Value == "") { - return "fp (_ bv" + (this.isNeg ? "1" : "0") + " 1) (_ bv" + this.exponent + " " + this.exponentSize + ") (_ bv" + this.significand + " " + (this.significandSize-1) + ")"; + + if (x.exponent > y.exponent) { + BigFloat temp = x; + x = y; + y = temp; } - else { - return "(_ to_fp " + this.exponentSize + " " + this.significandSize + ") (_ bv" + this.value + " " + (this.exponentSize + this.significandSize).ToString() + ")"; + + BIM xsig = x.significand, ysig = y.significand; + BIM xexp = x.exponent, yexp = y.exponent; + + if (yexp - xexp > x.significandSize) //One of the numbers is relatively insignificant + { + return new BigFloat(y.isSignBitSet, y.significand, y.exponent, y.significandSize, y.exponentSize); } - } - [Pure] - public string ToDecimalString() { - Contract.Ensures(Contract.Result() != null); - return value=="" ? String.Format("{0}x2^{1}", significand.ToString(), Exponent.ToString()) : value; - } + BIM hiddenBitPow = BIM.Pow(2, x.significandSize - 1); - [Pure] - public static string Zeros(int n) { - //TODO: fix for fp functionality - Contract.Requires(0 <= n); - if (n <= 10) { - var tenZeros = "0000000000"; - return tenZeros.Substring(0, n); + if (xexp > 0) { + xsig += hiddenBitPow; } else { - var d = n / 2; - var s = Zeros(d); - if (n % 2 == 0) { - return s + s; - } else { - return s + s + "0"; - } + ++xexp; } - } + if (yexp > 0) { + ysig += hiddenBitPow; + } else { + ++yexp; + } - //////////////////////////////////////////////////////////////////////////// - // Basic arithmetic operations + if (x.isSignBitSet) { + xsig = -xsig; + } + if (y.isSignBitSet) { + ysig = -ysig; + } - [Pure] - public BigFloat Abs { - get { - return new BigFloat(true, Exponent, Significand, ExponentSize, SignificandSize); + xsig >>= (int) (yexp - xexp); //Guaranteed to fit in a 32-bit integer + + ysig += xsig; + + bool isNeg = ysig < 0; + + ysig = BIM.Abs(ysig); + + if (ysig == 0) { + return new BigFloat(x.isSignBitSet && y.isSignBitSet, 0, 0, x.significandSize, x.exponentSize); } - } - [Pure] - public BigFloat Negate { - get { - if (value != "") - return value[0] == '-' ? new BigFloat(value.Remove(0, 1), ExponentSize, significandSize) : new BigFloat("-" + value, ExponentSize, significandSize); - return new BigFloat(!isNeg, Exponent, Significand, ExponentSize, SignificandSize); + if (ysig >= hiddenBitPow * 2) { + ysig >>= 1; + ++yexp; } - } - [Pure] - public static BigFloat operator -(BigFloat x) { - return x.Negate; - } + while (ysig < hiddenBitPow && yexp > 1) { + ysig <<= 1; + --yexp; + } - [Pure] - public static BigFloat operator +(BigFloat x, BigFloat y) { - //TODO: Modify for correct fp functionality - Contract.Requires(x.ExponentSize == y.ExponentSize); - Contract.Requires(x.significandSize == y.significandSize); - BIM m1 = x.significand; - BIM e1 = x.Exponent; - BIM m2 = y.significand; - BIM e2 = y.Exponent; - m1 = m1 + two_n(x.significandSize + 1); //Add implicit bit - m2 = m2 + two_n(y.significandSize + 1); - if (e2 > e1) { - m1 = y.significand; - e1 = y.Exponent; - m2 = x.significand; - e2 = x.Exponent; - } - - while (e2 < e1) { - m2 = m2 / two; - e2 = e2 + one; + if (ysig < hiddenBitPow) { + yexp = 0; + } else { + ysig -= hiddenBitPow; } - return new BigFloat(false, e1, m1 + m2, x.significandSize, x.ExponentSize); + if (yexp >= BIM.Pow(2, x.exponentSize) - 1) { + return new BigFloat(y.isSignBitSet ? "-oo" : "+oo", x.significandSize, x.exponentSize); + } + + return new BigFloat(isNeg, ysig, yexp, x.significandSize, x.exponentSize); } [Pure] public static BigFloat operator -(BigFloat x, BigFloat y) { - return x + y.Negate; + return x + -y; } [Pure] public static BigFloat operator *(BigFloat x, BigFloat y) { - Contract.Requires(x.ExponentSize == y.ExponentSize); + Contract.Requires(x.exponentSize == y.exponentSize); Contract.Requires(x.significandSize == y.significandSize); - return new BigFloat(x.isNeg ^ y.isNeg, x.Exponent + y.Exponent, x.significand * y.significand, x.significandSize, x.ExponentSize); - } + if (x.value == "NaN" || y.value == "NaN" || (x.value == "+oo" || x.value == "-oo") && y.IsZero || (y.value == "+oo" || y.value == "-oo") && x.IsZero) { + return new BigFloat("NaN", x.significandSize, x.exponentSize); + } - //////////////////////////////////////////////////////////////////////////// - // Some basic comparison operations + if (x.value != "" || y.value != "") { + bool xSignBitSet = x.value == "" ? x.isSignBitSet : x.value[0] == '-'; + bool ySignBitSet = y.value == "" ? y.isSignBitSet : y.value[0] == '-'; + return new BigFloat((xSignBitSet ^ ySignBitSet ? "-" : "+") + "oo", x.significandSize, x.exponentSize); + } - public bool IsSpecialType { - get { - if (value == "") - return false; - return (value.Equals("NaN") || value.Equals("+oo") || value.Equals("-oo") || value.Equals("zero") || value.Equals("-zero")); + BIM xsig = x.significand, ysig = y.significand; + BIM xexp = x.exponent, yexp = y.exponent; + + BIM hiddenBitPow = BIM.Pow(2, x.significandSize - 1); + + if (xexp > 0) { + xsig += hiddenBitPow; + } else { + ++xexp; } - } - public bool IsPositive { - get { - return !IsNegative; + if (yexp > 0) { + ysig += hiddenBitPow; + } else { + ++yexp; + } + + ysig *= xsig; + yexp += xexp - (BIM.Pow(2, x.exponentSize - 1) - 1) - (x.significandSize - 1); + + while (ysig >= hiddenBitPow * 2 || yexp <= 0) { + ysig >>= 1; + ++yexp; + } + + while (ysig < hiddenBitPow && yexp > 1) { + ysig <<= 1; + --yexp; + } + + if (ysig < hiddenBitPow) { + yexp = 0; + } else { + ysig -= hiddenBitPow; + } + + if (yexp >= BIM.Pow(2, x.exponentSize) - 1) { + return new BigFloat(x.isSignBitSet ^ y.isSignBitSet ? "-oo" : "+oo", x.significandSize, x.exponentSize); } + + return new BigFloat(x.isSignBitSet ^ y.isSignBitSet, ysig, yexp, x.significandSize, x.exponentSize); } + + //////////////////////////////////////////////////////////////////////////// + // Some basic comparison operations + public bool IsZero { get { - return significand.Equals(BigNum.ZERO) && Exponent == BIM.Zero; + return value == "" && significand.IsZero && exponent.IsZero; } } + /// + /// This method follows the specification for C#'s Single.CompareTo method. + /// As a result, it handles NaNs differently than how the ==, !=, <, >, <=, and >= operators do. + /// For example, the expression (0.0f / 0.0f).CompareTo(0.0f / 0.0f) should return 0, + /// whereas the expression (0.0f / 0.0f) == (0.0f / 0.0f) should return false. + /// [Pure] public int CompareTo(BigFloat that) { - if (this.exponent > that.exponent) - return 1; - if (this.exponent < that.exponent) - return -1; - if (this.significand == that.significand) + Contract.Requires(exponentSize == that.exponentSize); + Contract.Requires(significandSize == that.significandSize); + + if (value == "" && that.value == "") { + int cmpThis = IsZero ? 0 : isSignBitSet ? -1 : 1; + int cmpThat = that.IsZero ? 0 : that.isSignBitSet ? -1 : 1; + + if (cmpThis == cmpThat) { + if (exponent == that.exponent) { + return cmpThis * significand.CompareTo(that.significand); + } + + return cmpThis * exponent.CompareTo(that.exponent); + } + + if (cmpThis == 0) { + return -cmpThat; + } + + return cmpThis; + } + + if (value == that.value) { return 0; - return this.significand > that.significand ? 1 : -1; + } + + if (value == "NaN" || that.value == "+oo" || value == "-oo" && that.value != "NaN") { + return -1; + } + + return 1; } [Pure] public static bool operator ==(BigFloat x, BigFloat y) { + if (x.value == "NaN" || y.value == "NaN") { + return false; + } + return x.CompareTo(y) == 0; } [Pure] public static bool operator !=(BigFloat x, BigFloat y) { + if (x.value == "NaN" || y.value == "NaN") { + return true; + } + return x.CompareTo(y) != 0; } [Pure] public static bool operator <(BigFloat x, BigFloat y) { + if (x.value == "NaN" || y.value == "NaN") { + return false; + } + return x.CompareTo(y) < 0; } [Pure] public static bool operator >(BigFloat x, BigFloat y) { + if (x.value == "NaN" || y.value == "NaN") { + return false; + } + return x.CompareTo(y) > 0; } [Pure] public static bool operator <=(BigFloat x, BigFloat y) { + if (x.value == "NaN" || y.value == "NaN") { + return false; + } + return x.CompareTo(y) <= 0; } [Pure] public static bool operator >=(BigFloat x, BigFloat y) { + if (x.value == "NaN" || y.value == "NaN") { + return false; + } + return x.CompareTo(y) >= 0; } } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Basetypes/RoundingMode.cs boogie-2.4.1+dfsg/Source/Basetypes/RoundingMode.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Basetypes/RoundingMode.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Basetypes/RoundingMode.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Text; +using System.Diagnostics.Contracts; + +namespace Microsoft.Basetypes +{ + + /// + /// A representation of a rounding mode + /// + public class RoundingMode + { + private string val; + + public static readonly RoundingMode RNE = new RoundingMode("RNE"); + public static readonly RoundingMode RNA = new RoundingMode("RNA"); + public static readonly RoundingMode RTP = new RoundingMode("RTP"); + public static readonly RoundingMode RTN = new RoundingMode("RTN"); + public static readonly RoundingMode RTZ = new RoundingMode("RTZ"); + + private RoundingMode(string val) + { + this.val = val; + } + + [Pure] + public static RoundingMode FromString(String s) + { + if (s.Equals("RNE") || s.Equals("roundNearestTiesToEven")) return RNE; + if (s.Equals("RNA") || s.Equals("roundNearestTiesToAway")) return RNA; + if (s.Equals("RTP") || s.Equals("roundTowardPositive")) return RTP; + if (s.Equals("RTN") || s.Equals("roundTowardNegative")) return RTN; + if (s.Equals("RTZ") || s.Equals("roundTowardZero")) return RTZ; + throw new FormatException(s + " is not a valid rounding mode."); + } + + [Pure] + [Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) + { + RoundingMode rm = obj as RoundingMode; + return rm != null && this == rm; + } + + [Pure] + public override int GetHashCode() + { + return val.GetHashCode(); + } + + [Pure] + public override string/*!*/ ToString() + { + Contract.Ensures(Contract.Result() != null); + return val; + } + + [Pure] + public static bool operator ==(RoundingMode a, RoundingMode b) + { + return a.val.Equals(b.val); + } + + [Pure] + public static bool operator !=(RoundingMode a, RoundingMode b) + { + return !a.val.Equals(b.val); + } + } +} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/BoogieDriver/app.config boogie-2.4.1+dfsg/Source/BoogieDriver/app.config --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/BoogieDriver/app.config 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/BoogieDriver/app.config 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,3 @@ + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/BoogieDriver/BoogieDriver.csproj boogie-2.4.1+dfsg/Source/BoogieDriver/BoogieDriver.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/BoogieDriver/BoogieDriver.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/BoogieDriver/BoogieDriver.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,7 +10,7 @@ Properties BoogieDriver Boogie - v4.0 + v4.5 512 1 true @@ -74,6 +74,7 @@ Full %28none%29 AllRules.ruleset + false pdbonly @@ -83,6 +84,7 @@ prompt 4 AllRules.ruleset + false true @@ -99,6 +101,7 @@ true 4 false + false true @@ -141,6 +144,7 @@ 0 4 false + false true @@ -158,6 +162,7 @@ ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false + false bin\x86\Release\ @@ -175,6 +180,7 @@ ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 + false true @@ -192,6 +198,7 @@ true 4 false + false true @@ -210,6 +217,7 @@ false 4 false + false true @@ -219,6 +227,7 @@ AnyCPU prompt AllRules.ruleset + false true @@ -229,6 +238,7 @@ prompt AllRules.ruleset false + false @@ -313,6 +323,9 @@ true + + + - + \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/CodeContractsExtender/CodeContractsExtender.csproj boogie-2.4.1+dfsg/Source/CodeContractsExtender/CodeContractsExtender.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/CodeContractsExtender/CodeContractsExtender.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/CodeContractsExtender/CodeContractsExtender.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,7 +10,7 @@ Properties CodeContractsExtender BoogieCodeContractsExtender - v4.0 + v4.5 512 true ..\InterimKey.snk @@ -34,7 +34,7 @@ false false true - Client + true @@ -74,6 +74,7 @@ Full %28none%29 AllRules.ruleset + false pdbonly @@ -83,6 +84,7 @@ prompt 4 AllRules.ruleset + false true @@ -99,6 +101,7 @@ true 4 false + false true @@ -141,6 +144,7 @@ 0 4 false + false true @@ -150,6 +154,7 @@ AnyCPU prompt AllRules.ruleset + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/CodeContractsExtender/CodeContractsExtender-NetCore.csproj boogie-2.4.1+dfsg/Source/CodeContractsExtender/CodeContractsExtender-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/CodeContractsExtender/CodeContractsExtender-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/CodeContractsExtender/CodeContractsExtender-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,11 @@ + + + + Library + BoogieCodeContractsExtender + netcoreapp2.2 + COREFX_SUBSET + false + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/App.config boogie-2.4.1+dfsg/Source/Concurrency/App.config --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/App.config 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/App.config 2019-10-25 18:17:05.000000000 +0000 @@ -1,6 +1,6 @@ - + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/CivlRefinement.cs boogie-2.4.1+dfsg/Source/Concurrency/CivlRefinement.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/CivlRefinement.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/CivlRefinement.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,1230 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Boogie; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using Microsoft.Boogie.GraphUtil; - -namespace Microsoft.Boogie -{ - public class MyDuplicator : Duplicator - { - CivlTypeChecker civlTypeChecker; - public int layerNum; - Procedure enclosingProc; - Implementation enclosingImpl; - public Dictionary procMap; /* Original -> Duplicate */ - public Dictionary absyMap; /* Duplicate -> Original */ - public Dictionary implMap; /* Duplicate -> Original */ - public HashSet yieldingProcs; - public List impls; - - public MyDuplicator(CivlTypeChecker civlTypeChecker, int layerNum) - { - this.civlTypeChecker = civlTypeChecker; - this.layerNum = layerNum; - this.enclosingProc = null; - this.enclosingImpl = null; - this.procMap = new Dictionary(); - this.absyMap = new Dictionary(); - this.implMap = new Dictionary(); - this.yieldingProcs = new HashSet(); - this.impls = new List(); - } - - private void ProcessCallCmd(CallCmd originalCallCmd, CallCmd callCmd, List newCmds) - { - int enclosingProcLayerNum = civlTypeChecker.procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; - Procedure originalProc = originalCallCmd.Proc; - - if (civlTypeChecker.procToAtomicProcedureInfo.ContainsKey(originalProc)) - { - if (civlTypeChecker.CallExists(originalCallCmd, enclosingProcLayerNum, layerNum)) - { - newCmds.Add(callCmd); - } - } - else if (civlTypeChecker.procToActionInfo.ContainsKey(originalProc)) - { - AtomicActionInfo atomicActionInfo = civlTypeChecker.procToActionInfo[originalProc] as AtomicActionInfo; - if (atomicActionInfo != null && atomicActionInfo.gate.Count > 0 && layerNum == enclosingProcLayerNum) - { - newCmds.Add(new HavocCmd(Token.NoToken, new List(new IdentifierExpr[] { Expr.Ident(dummyLocalVar) }))); - Dictionary map = new Dictionary(); - for (int i = 0; i < originalProc.InParams.Count; i++) - { - map[originalProc.InParams[i]] = callCmd.Ins[i]; - } - Substitution subst = Substituter.SubstitutionFromHashtable(map); - foreach (AssertCmd assertCmd in atomicActionInfo.gate) - { - newCmds.Add(Substituter.Apply(subst, assertCmd)); - } - } - newCmds.Add(callCmd); - } - else - { - Debug.Assert(false); - } - } - - private void ProcessParCallCmd(ParCallCmd originalParCallCmd, ParCallCmd parCallCmd, List newCmds) - { - int maxCalleeLayerNum = 0; - foreach (CallCmd iter in originalParCallCmd.CallCmds) - { - int calleeLayerNum = civlTypeChecker.procToActionInfo[iter.Proc].createdAtLayerNum; - if (calleeLayerNum > maxCalleeLayerNum) - maxCalleeLayerNum = calleeLayerNum; - } - if (layerNum > maxCalleeLayerNum) - { - for (int i = 0; i < parCallCmd.CallCmds.Count; i++) - { - ProcessCallCmd(originalParCallCmd.CallCmds[i], parCallCmd.CallCmds[i], newCmds); - absyMap[parCallCmd.CallCmds[i]] = originalParCallCmd; - } - } - else - { - newCmds.Add(parCallCmd); - } - } - - public override List VisitCmdSeq(List cmdSeq) - { - List cmds = base.VisitCmdSeq(cmdSeq); - List newCmds = new List(); - for (int i = 0; i < cmds.Count; i++) - { - Cmd originalCmd = cmdSeq[i]; - Cmd cmd = cmds[i]; - - CallCmd originalCallCmd = originalCmd as CallCmd; - if (originalCallCmd != null) - { - ProcessCallCmd(originalCallCmd, cmd as CallCmd, newCmds); - continue; - } - - ParCallCmd originalParCallCmd = originalCmd as ParCallCmd; - if (originalParCallCmd != null) - { - ProcessParCallCmd(originalParCallCmd, cmd as ParCallCmd, newCmds); - continue; - } - - newCmds.Add(cmd); - } - return newCmds; - } - - public override YieldCmd VisitYieldCmd(YieldCmd node) - { - YieldCmd yieldCmd = base.VisitYieldCmd(node); - absyMap[yieldCmd] = node; - return yieldCmd; - } - - public override Block VisitBlock(Block node) - { - Block block = base.VisitBlock(node); - absyMap[block] = node; - return block; - } - - public override Cmd VisitCallCmd(CallCmd node) - { - CallCmd callCmd = (CallCmd) base.VisitCallCmd(node); - callCmd.Proc = VisitProcedure(callCmd.Proc); - callCmd.callee = callCmd.Proc.Name; - absyMap[callCmd] = node; - return callCmd; - } - - public override Cmd VisitParCallCmd(ParCallCmd node) - { - ParCallCmd parCallCmd = (ParCallCmd) base.VisitParCallCmd(node); - absyMap[parCallCmd] = node; - return parCallCmd; - } - - public override Procedure VisitProcedure(Procedure node) - { - if (!civlTypeChecker.procToActionInfo.ContainsKey(node)) - return node; - if (!procMap.ContainsKey(node)) - { - enclosingProc = node; - Procedure proc = (Procedure)node.Clone(); - proc.Name = string.Format("{0}_{1}", node.Name, layerNum); - proc.InParams = this.VisitVariableSeq(node.InParams); - proc.Modifies = this.VisitIdentifierExprSeq(node.Modifies); - proc.OutParams = this.VisitVariableSeq(node.OutParams); - - ActionInfo actionInfo = civlTypeChecker.procToActionInfo[node]; - if (actionInfo.createdAtLayerNum < layerNum) - { - proc.Requires = new List(); - proc.Ensures = new List(); - Implementation impl; - AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; - if (atomicActionInfo != null) - { - CodeExpr action = (CodeExpr)VisitCodeExpr(atomicActionInfo.action); - List cmds = new List(); - foreach (AssertCmd assertCmd in atomicActionInfo.gate) - { - cmds.Add(new AssumeCmd(Token.NoToken, (Expr)Visit(assertCmd.Expr))); - } - Block newInitBlock = new Block(Token.NoToken, "_init", cmds, - new GotoCmd(Token.NoToken, new List(new string[] { action.Blocks[0].Label }), - new List(new Block[] { action.Blocks[0] }))); - List newBlocks = new List(); - newBlocks.Add(newInitBlock); - newBlocks.AddRange(action.Blocks); - impl = new Implementation(Token.NoToken, proc.Name, node.TypeParameters, node.InParams, node.OutParams, action.LocVars, newBlocks); - } - else - { - Block newInitBlock = new Block(Token.NoToken, "_init", new List(), new ReturnCmd(Token.NoToken)); - List newBlocks = new List(); - newBlocks.Add(newInitBlock); - impl = new Implementation(Token.NoToken, proc.Name, node.TypeParameters, node.InParams, node.OutParams, new List(), newBlocks); - } - impl.Proc = proc; - impl.Proc.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - impl.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - impls.Add(impl); - } - else - { - yieldingProcs.Add(proc); - proc.Requires = this.VisitRequiresSeq(node.Requires); - proc.Ensures = this.VisitEnsuresSeq(node.Ensures); - } - procMap[node] = proc; - proc.Modifies = new List(); - civlTypeChecker.SharedVariables.Iter(x => proc.Modifies.Add(Expr.Ident(x))); - } - return procMap[node]; - } - - private Variable dummyLocalVar; - public override Implementation VisitImplementation(Implementation node) - { - enclosingImpl = node; - dummyLocalVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_dummy", Type.Bool)); - Implementation impl = base.VisitImplementation(node); - implMap[impl] = node; - impl.LocVars.Add(dummyLocalVar); - impl.Name = impl.Proc.Name; - return impl; - } - - public override Requires VisitRequires(Requires node) - { - Requires requires = base.VisitRequires(node); - if (node.Free) - return requires; - if (!civlTypeChecker.absyToLayerNums[node].Contains(layerNum)) - requires.Condition = Expr.True; - return requires; - } - - public override Ensures VisitEnsures(Ensures node) - { - Ensures ensures = base.VisitEnsures(node); - if (node.Free) - return ensures; - AtomicActionInfo atomicActionInfo = civlTypeChecker.procToActionInfo[enclosingProc] as AtomicActionInfo; - bool isAtomicSpecification = atomicActionInfo != null && atomicActionInfo.ensures == node; - if (isAtomicSpecification || !civlTypeChecker.absyToLayerNums[node].Contains(layerNum)) - { - ensures.Condition = Expr.True; - ensures.Attributes = CivlRefinement.RemoveMoverAttribute(ensures.Attributes); - } - return ensures; - } - - public override Cmd VisitAssertCmd(AssertCmd node) - { - AssertCmd assertCmd = (AssertCmd) base.VisitAssertCmd(node); - if (!civlTypeChecker.absyToLayerNums[node].Contains(layerNum)) - assertCmd.Expr = Expr.True; - return assertCmd; - } - } - - public class CivlRefinement - { - LinearTypeChecker linearTypeChecker; - CivlTypeChecker civlTypeChecker; - Dictionary absyMap; - Dictionary implMap; - HashSet yieldingProcs; - int layerNum; - List globalMods; - Dictionary asyncAndParallelCallDesugarings; - List yieldCheckerProcs; - List yieldCheckerImpls; - Procedure yieldProc; - - Variable pc; - Variable ok; - Expr alpha; - Expr beta; - HashSet frame; - - public CivlRefinement(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, MyDuplicator duplicator) - { - this.linearTypeChecker = linearTypeChecker; - this.civlTypeChecker = civlTypeChecker; - this.absyMap = duplicator.absyMap; - this.layerNum = duplicator.layerNum; - this.implMap = duplicator.implMap; - this.yieldingProcs = duplicator.yieldingProcs; - Program program = linearTypeChecker.program; - globalMods = new List(); - foreach (Variable g in civlTypeChecker.SharedVariables) - { - globalMods.Add(Expr.Ident(g)); - } - asyncAndParallelCallDesugarings = new Dictionary(); - yieldCheckerProcs = new List(); - yieldCheckerImpls = new List(); - yieldProc = null; - } - - private IEnumerable AvailableLinearVars(Absy absy) - { - HashSet availableVars = new HashSet(linearTypeChecker.AvailableLinearVars(absyMap[absy])); - foreach (var g in civlTypeChecker.globalVarToSharedVarInfo.Keys) - { - SharedVariableInfo info = civlTypeChecker.globalVarToSharedVarInfo[g]; - if (!(info.introLayerNum <= layerNum && layerNum <= info.hideLayerNum)) - { - availableVars.Remove(g); - } - } - foreach (var v in civlTypeChecker.localVarToLocalVariableInfo.Keys) - { - LocalVariableInfo info = civlTypeChecker.localVarToLocalVariableInfo[v]; - if (layerNum < info.layer) - { - availableVars.Remove(v); - } - } - return availableVars; - } - - private CallCmd CallToYieldProc(IToken tok, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) - { - List exprSeq = new List(); - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - exprSeq.Add(Expr.Ident(domainNameToLocalVar[domainName])); - } - foreach (IdentifierExpr ie in globalMods) - { - exprSeq.Add(Expr.Ident(ogOldGlobalMap[ie.Decl])); - } - if (yieldProc == null) - { - List inputs = new List(); - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - var domain = linearTypeChecker.linearDomains[domainName]; - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_in", new MapType(Token.NoToken, new List(), new List { domain.elementType }, Type.Bool)), true); - inputs.Add(f); - } - foreach (IdentifierExpr ie in globalMods) - { - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", ie.Decl.Name), ie.Decl.TypedIdent.Type), true); - inputs.Add(f); - } - yieldProc = new Procedure(Token.NoToken, string.Format("og_yield_{0}", layerNum), new List(), inputs, new List(), new List(), new List(), new List()); - yieldProc.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - } - CallCmd yieldCallCmd = new CallCmd(Token.NoToken, yieldProc.Name, exprSeq, new List()); - yieldCallCmd.Proc = yieldProc; - return yieldCallCmd; - } - - private void AddCallToYieldProc(IToken tok, List newCmds, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) - { - if (!CommandLineOptions.Clo.TrustNonInterference) - { - CallCmd yieldCallCmd = CallToYieldProc(tok, ogOldGlobalMap, domainNameToLocalVar); - newCmds.Add(yieldCallCmd); - } - - if (pc != null) - { - Expr aa = OldEqualityExprForGlobals(ogOldGlobalMap); - Expr bb = OldEqualityExpr(ogOldGlobalMap); - - // assert pc || g_old == g || beta(i, g_old, o, g); - Expr assertExpr = Expr.Or(Expr.Ident(pc), Expr.Or(aa, beta)); - assertExpr.Typecheck(new TypecheckingContext(null)); - AssertCmd skipOrBetaAssertCmd = new AssertCmd(tok, assertExpr); - skipOrBetaAssertCmd.ErrorData = "Transition invariant in initial state violated"; - newCmds.Add(skipOrBetaAssertCmd); - - // assert pc ==> o_old == o && g_old == g; - assertExpr = Expr.Imp(Expr.Ident(pc), bb); - assertExpr.Typecheck(new TypecheckingContext(null)); - AssertCmd skipAssertCmd = new AssertCmd(tok, assertExpr); - skipAssertCmd.ErrorData = "Transition invariant in final state violated"; ; - newCmds.Add(skipAssertCmd); - - // pc, ok := g_old == g ==> pc, ok || beta(i, g_old, o, g); - List pcUpdateLHS = new List( - new AssignLhs[] { - new SimpleAssignLhs(Token.NoToken, Expr.Ident(pc)), - new SimpleAssignLhs(Token.NoToken, Expr.Ident(ok)) - }); - List pcUpdateRHS = new List( - new Expr[] { - Expr.Imp(aa, Expr.Ident(pc)), - Expr.Or(Expr.Ident(ok), beta) - }); - foreach (Expr e in pcUpdateRHS) - { - e.Typecheck(new TypecheckingContext(null)); - } - newCmds.Add(new AssignCmd(Token.NoToken, pcUpdateLHS, pcUpdateRHS)); - } - } - - private Dictionary ComputeAvailableExprs(IEnumerable availableLinearVars, Dictionary domainNameToInputVar) - { - Dictionary domainNameToExpr = new Dictionary(); - foreach (var domainName in linearTypeChecker.linearDomains.Keys) - { - var expr = Expr.Ident(domainNameToInputVar[domainName]); - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); - domainNameToExpr[domainName] = expr; - } - foreach (Variable v in availableLinearVars) - { - var domainName = linearTypeChecker.FindDomainName(v); - if (!linearTypeChecker.linearDomains.ContainsKey(domainName)) continue; - var domain = linearTypeChecker.linearDomains[domainName]; - if (!domain.collectors.ContainsKey(v.TypedIdent.Type)) continue; - Expr ie = new NAryExpr(Token.NoToken, new FunctionCall(domain.collectors[v.TypedIdent.Type]), new List { Expr.Ident(v) }); - var expr = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapOrBool), new List { ie, domainNameToExpr[domainName] }); - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); - domainNameToExpr[domainName] = expr; - } - return domainNameToExpr; - } - - private void AddUpdatesToOldGlobalVars(List newCmds, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar, Dictionary domainNameToExpr) - { - List lhss = new List(); - List rhss = new List(); - foreach (var domainName in linearTypeChecker.linearDomains.Keys) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(domainNameToLocalVar[domainName]))); - rhss.Add(domainNameToExpr[domainName]); - } - foreach (Variable g in ogOldGlobalMap.Keys) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ogOldGlobalMap[g]))); - rhss.Add(Expr.Ident(g)); - } - if (lhss.Count > 0) - { - newCmds.Add(new AssignCmd(Token.NoToken, lhss, rhss)); - } - } - - private Expr OldEqualityExpr(Dictionary ogOldGlobalMap) - { - Expr bb = Expr.True; - foreach (Variable o in ogOldGlobalMap.Keys) - { - if (o is GlobalVariable && !frame.Contains(o)) continue; - bb = Expr.And(bb, Expr.Eq(Expr.Ident(o), Expr.Ident(ogOldGlobalMap[o]))); - bb.Type = Type.Bool; - } - return bb; - } - - private Expr OldEqualityExprForGlobals(Dictionary ogOldGlobalMap) - { - Expr bb = Expr.True; - foreach (Variable o in ogOldGlobalMap.Keys) - { - if (o is GlobalVariable && frame.Contains(o)) - { - bb = Expr.And(bb, Expr.Eq(Expr.Ident(o), Expr.Ident(ogOldGlobalMap[o]))); - bb.Type = Type.Bool; - } - } - return bb; - } - - private void DesugarYield(YieldCmd yieldCmd, List cmds, List newCmds, Dictionary ogOldGlobalMap, Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar) - { - AddCallToYieldProc(yieldCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); - - if (globalMods.Count > 0) - { - newCmds.Add(new HavocCmd(Token.NoToken, globalMods)); - if (pc != null) - { - // assume pc || alpha(i, g); - Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); - assumeExpr.Type = Type.Bool; - newCmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); - } - } - - Dictionary domainNameToExpr = ComputeAvailableExprs(AvailableLinearVars(yieldCmd), domainNameToInputVar); - AddUpdatesToOldGlobalVars(newCmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); - - for (int j = 0; j < cmds.Count; j++) - { - PredicateCmd predCmd = (PredicateCmd)cmds[j]; - newCmds.Add(new AssumeCmd(Token.NoToken, predCmd.Expr)); - } - } - - public void DesugarParallelCallCmd(List newCmds, ParCallCmd parCallCmd) - { - List parallelCalleeNames = new List(); - List ins = new List(); - List outs = new List(); - string procName = "og"; - foreach (CallCmd callCmd in parCallCmd.CallCmds) - { - procName = procName + "_" + callCmd.Proc.Name; - ins.AddRange(callCmd.Ins); - outs.AddRange(callCmd.Outs); - } - Procedure proc; - if (asyncAndParallelCallDesugarings.ContainsKey(procName)) - { - proc = asyncAndParallelCallDesugarings[procName]; - } - else - { - List inParams = new List(); - List outParams = new List(); - List requiresSeq = new List(); - List ensuresSeq = new List(); - int count = 0; - foreach (CallCmd callCmd in parCallCmd.CallCmds) - { - Dictionary map = new Dictionary(); - foreach (Variable x in callCmd.Proc.InParams) - { - Variable y = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_{0}_{1}", count, x.Name), x.TypedIdent.Type), true); - inParams.Add(y); - map[x] = Expr.Ident(y); - } - foreach (Variable x in callCmd.Proc.OutParams) - { - Variable y = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_{0}_{1}", count, x.Name), x.TypedIdent.Type), false); - outParams.Add(y); - map[x] = Expr.Ident(y); - } - Contract.Assume(callCmd.Proc.TypeParameters.Count == 0); - Substitution subst = Substituter.SubstitutionFromHashtable(map); - foreach (Requires req in callCmd.Proc.Requires) - { - requiresSeq.Add(new Requires(req.tok, req.Free, Substituter.Apply(subst, req.Condition), null, req.Attributes)); - } - foreach (Ensures ens in callCmd.Proc.Ensures) - { - ensuresSeq.Add(new Ensures(ens.tok, ens.Free, Substituter.Apply(subst, ens.Condition), null, ens.Attributes)); - } - count++; - } - proc = new Procedure(Token.NoToken, procName, new List(), inParams, outParams, requiresSeq, globalMods, ensuresSeq); - asyncAndParallelCallDesugarings[procName] = proc; - } - CallCmd dummyCallCmd = new CallCmd(parCallCmd.tok, proc.Name, ins, outs, parCallCmd.Attributes); - dummyCallCmd.Proc = proc; - newCmds.Add(dummyCallCmd); - } - - private void CreateYieldCheckerImpl(Implementation impl, List> yields) - { - if (yields.Count == 0) return; - - Dictionary map = new Dictionary(); - foreach (Variable local in impl.LocVars) - { - var copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, local.Name, local.TypedIdent.Type)); - map[local] = Expr.Ident(copy); - } - - Program program = linearTypeChecker.program; - List locals = new List(); - List inputs = new List(); - foreach (IdentifierExpr ie in map.Values) - { - locals.Add(ie.Decl); - } - for (int i = 0; i < impl.InParams.Count - linearTypeChecker.linearDomains.Count; i++) - { - Variable inParam = impl.InParams[i]; - Variable copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, inParam.Name, inParam.TypedIdent.Type)); - locals.Add(copy); - map[impl.InParams[i]] = Expr.Ident(copy); - } - { - int i = impl.InParams.Count - linearTypeChecker.linearDomains.Count; - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - Variable inParam = impl.InParams[i]; - Variable copy = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, inParam.Name, inParam.TypedIdent.Type), true); - inputs.Add(copy); - map[impl.InParams[i]] = Expr.Ident(copy); - i++; - } - } - for (int i = 0; i < impl.OutParams.Count; i++) - { - Variable outParam = impl.OutParams[i]; - var copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, outParam.Name, outParam.TypedIdent.Type)); - locals.Add(copy); - map[impl.OutParams[i]] = Expr.Ident(copy); - } - Dictionary ogOldLocalMap = new Dictionary(); - Dictionary assumeMap = new Dictionary(map); - foreach (IdentifierExpr ie in globalMods) - { - Variable g = ie.Decl; - var copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_local_old_{0}", g.Name), g.TypedIdent.Type)); - locals.Add(copy); - ogOldLocalMap[g] = Expr.Ident(copy); - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", g.Name), g.TypedIdent.Type), true); - inputs.Add(f); - assumeMap[g] = Expr.Ident(f); - } - - Substitution assumeSubst = Substituter.SubstitutionFromHashtable(assumeMap); - Substitution oldSubst = Substituter.SubstitutionFromHashtable(ogOldLocalMap); - Substitution subst = Substituter.SubstitutionFromHashtable(map); - List yieldCheckerBlocks = new List(); - List labels = new List(); - List labelTargets = new List(); - Block yieldCheckerBlock = new Block(Token.NoToken, "exit", new List(), new ReturnCmd(Token.NoToken)); - labels.Add(yieldCheckerBlock.Label); - labelTargets.Add(yieldCheckerBlock); - yieldCheckerBlocks.Add(yieldCheckerBlock); - int yieldCount = 0; - foreach (List cs in yields) - { - List newCmds = new List(); - foreach (Cmd cmd in cs) - { - PredicateCmd predCmd = (PredicateCmd)cmd; - newCmds.Add(new AssumeCmd(Token.NoToken, Substituter.ApplyReplacingOldExprs(assumeSubst, oldSubst, predCmd.Expr))); - } - foreach (Cmd cmd in cs) - { - PredicateCmd predCmd = (PredicateCmd)cmd; - var newExpr = Substituter.ApplyReplacingOldExprs(subst, oldSubst, predCmd.Expr); - if (predCmd is AssertCmd) - { - AssertCmd assertCmd = new AssertCmd(predCmd.tok, newExpr, predCmd.Attributes); - assertCmd.ErrorData = "Non-interference check failed"; - newCmds.Add(assertCmd); - } - else - { - newCmds.Add(new AssumeCmd(Token.NoToken, newExpr)); - } - } - newCmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); - yieldCheckerBlock = new Block(Token.NoToken, "L" + yieldCount++, newCmds, new ReturnCmd(Token.NoToken)); - labels.Add(yieldCheckerBlock.Label); - labelTargets.Add(yieldCheckerBlock); - yieldCheckerBlocks.Add(yieldCheckerBlock); - } - yieldCheckerBlocks.Insert(0, new Block(Token.NoToken, "enter", new List(), new GotoCmd(Token.NoToken, labels, labelTargets))); - - // Create the yield checker procedure - var yieldCheckerName = string.Format("{0}_YieldChecker_{1}", "Impl", impl.Name); - var yieldCheckerProc = new Procedure(Token.NoToken, yieldCheckerName, impl.TypeParameters, inputs, new List(), new List(), new List(), new List()); - yieldCheckerProc.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - yieldCheckerProcs.Add(yieldCheckerProc); - - // Create the yield checker implementation - var yieldCheckerImpl = new Implementation(Token.NoToken, yieldCheckerName, impl.TypeParameters, inputs, new List(), locals, yieldCheckerBlocks); - yieldCheckerImpl.Proc = yieldCheckerProc; - yieldCheckerImpl.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - yieldCheckerImpls.Add(yieldCheckerImpl); - } - - private bool IsYieldingHeader(Graph graph, Block header) - { - foreach (Block backEdgeNode in graph.BackEdgeNodes(header)) - { - foreach (Block x in graph.NaturalLoops(header, backEdgeNode)) - { - foreach (Cmd cmd in x.Cmds) - { - if (cmd is YieldCmd) - return true; - if (cmd is ParCallCmd) - return true; - CallCmd callCmd = cmd as CallCmd; - if (callCmd == null) continue; - if (yieldingProcs.Contains(callCmd.Proc)) - return true; - } - } - } - return false; - } - - private Graph ComputeYieldingLoopHeaders(Implementation impl, out HashSet yieldingHeaders) - { - Graph graph; - impl.PruneUnreachableBlocks(); - impl.ComputePredecessorsForBlocks(); - graph = Program.GraphFromImpl(impl); - graph.ComputeLoops(); - if (!graph.Reducible) - { - throw new Exception("Irreducible flow graphs are unsupported."); - } - yieldingHeaders = new HashSet(); - IEnumerable sortedHeaders = graph.SortHeadersByDominance(); - foreach (Block header in sortedHeaders) - { - if (yieldingHeaders.Any(x => graph.DominatorMap.DominatedBy(x, header))) - { - yieldingHeaders.Add(header); - } - else if (IsYieldingHeader(graph, header)) - { - yieldingHeaders.Add(header); - } - else - { - continue; - } - } - return graph; - } - - private void SetupRefinementCheck(Implementation impl, - out List newLocalVars, - out Dictionary domainNameToInputVar, out Dictionary domainNameToLocalVar, out Dictionary ogOldGlobalMap) - { - pc = null; - ok = null; - alpha = null; - beta = null; - frame = null; - - newLocalVars = new List(); - Program program = linearTypeChecker.program; - ogOldGlobalMap = new Dictionary(); - foreach (IdentifierExpr ie in globalMods) - { - Variable g = ie.Decl; - LocalVariable l = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", g.Name), g.TypedIdent.Type)); - ogOldGlobalMap[g] = l; - newLocalVars.Add(l); - } - - Procedure originalProc = implMap[impl].Proc; - ActionInfo actionInfo = civlTypeChecker.procToActionInfo[originalProc]; - if (actionInfo.createdAtLayerNum == this.layerNum) - { - pc = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_pc", Type.Bool)); - newLocalVars.Add(pc); - ok = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_ok", Type.Bool)); - newLocalVars.Add(ok); - Dictionary alwaysMap = new Dictionary(); - for (int i = 0; i < originalProc.InParams.Count; i++) - { - alwaysMap[originalProc.InParams[i]] = Expr.Ident(impl.InParams[i]); - } - for (int i = 0; i < originalProc.OutParams.Count; i++) - { - alwaysMap[originalProc.OutParams[i]] = Expr.Ident(impl.OutParams[i]); - } - Substitution always = Substituter.SubstitutionFromHashtable(alwaysMap); - Dictionary foroldMap = new Dictionary(); - foreach (IdentifierExpr ie in globalMods) - { - foroldMap[ie.Decl] = Expr.Ident(ogOldGlobalMap[ie.Decl]); - } - Substitution forold = Substituter.SubstitutionFromHashtable(foroldMap); - frame = new HashSet(civlTypeChecker.SharedVariables); - foreach (Variable v in civlTypeChecker.SharedVariables) - { - if (civlTypeChecker.globalVarToSharedVarInfo[v].hideLayerNum <= actionInfo.createdAtLayerNum || - civlTypeChecker.globalVarToSharedVarInfo[v].introLayerNum > actionInfo.createdAtLayerNum) - { - frame.Remove(v); - } - } - AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; - if (atomicActionInfo == null) - { - beta = Expr.True; - foreach (var v in frame) - { - beta = Expr.And(beta, Expr.Eq(Expr.Ident(v), foroldMap[v])); - } - alpha = Expr.True; - } - else - { - Expr betaExpr = (new MoverCheck.TransitionRelationComputation(civlTypeChecker.program, atomicActionInfo, frame, new HashSet())).TransitionRelationCompute(true); - beta = Substituter.ApplyReplacingOldExprs(always, forold, betaExpr); - Expr alphaExpr = Expr.True; - foreach (AssertCmd assertCmd in atomicActionInfo.gate) - { - alphaExpr = Expr.And(alphaExpr, assertCmd.Expr); - alphaExpr.Type = Type.Bool; - } - alpha = Substituter.Apply(always, alphaExpr); - } - foreach (Variable f in impl.OutParams) - { - LocalVariable copy = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_old_{0}", f.Name), f.TypedIdent.Type)); - newLocalVars.Add(copy); - ogOldGlobalMap[f] = copy; - } - } - - domainNameToInputVar = new Dictionary(); - domainNameToLocalVar = new Dictionary(); - { - int i = impl.InParams.Count - linearTypeChecker.linearDomains.Count; - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - Variable inParam = impl.InParams[i]; - domainNameToInputVar[domainName] = inParam; - Variable l = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, inParam.Name + "_local", inParam.TypedIdent.Type)); - domainNameToLocalVar[domainName] = l; - newLocalVars.Add(l); - i++; - } - } - } - - private void TransformImpl(Implementation impl) - { - HashSet yieldingHeaders; - Graph graph = ComputeYieldingLoopHeaders(impl, out yieldingHeaders); - - List newLocalVars; - Dictionary domainNameToInputVar, domainNameToLocalVar; - Dictionary ogOldGlobalMap; - SetupRefinementCheck(impl, out newLocalVars, out domainNameToInputVar, out domainNameToLocalVar, out ogOldGlobalMap); - - List> yields = CollectAndDesugarYields(impl, domainNameToInputVar, domainNameToLocalVar, ogOldGlobalMap); - - List oldPcs, oldOks; - ProcessLoopHeaders(impl, graph, yieldingHeaders, domainNameToInputVar, domainNameToLocalVar, ogOldGlobalMap, out oldPcs, out oldOks); - - AddInitialBlock(impl, oldPcs, oldOks, domainNameToInputVar, domainNameToLocalVar, ogOldGlobalMap); - - CreateYieldCheckerImpl(impl, yields); - - impl.LocVars.AddRange(newLocalVars); - impl.LocVars.AddRange(oldPcs); - impl.LocVars.AddRange(oldOks); - - UnifyCallsToYieldProc(impl, ogOldGlobalMap, domainNameToLocalVar); - } - - private void UnifyCallsToYieldProc(Implementation impl, Dictionary ogOldGlobalMap, Dictionary domainNameToLocalVar) - { - CallCmd yieldCallCmd = CallToYieldProc(Token.NoToken, ogOldGlobalMap, domainNameToLocalVar); - Block yieldCheckBlock = new Block(Token.NoToken, "CallToYieldProc", new List(new Cmd[] { yieldCallCmd, new AssumeCmd(Token.NoToken, Expr.False) }), new ReturnCmd(Token.NoToken)); - List newBlocks = new List(); - foreach (Block b in impl.Blocks) - { - TransferCmd transferCmd = b.TransferCmd; - List newCmds = new List(); - for (int i = b.Cmds.Count-1; i >= 0; i--) - { - CallCmd callCmd = b.Cmds[i] as CallCmd; - if (callCmd == null || callCmd.Proc != yieldProc) - { - newCmds.Insert(0, b.Cmds[i]); - } - else - { - Block newBlock = new Block(Token.NoToken, b.Label + i, newCmds, transferCmd); - newCmds = new List(); - transferCmd = new GotoCmd(Token.NoToken, new List(new string[] { newBlock.Label, yieldCheckBlock.Label }), - new List(new Block[] { newBlock, yieldCheckBlock })); - newBlocks.Add(newBlock); - } - } - b.Cmds = newCmds; - b.TransferCmd = transferCmd; - } - impl.Blocks.AddRange(newBlocks); - impl.Blocks.Add(yieldCheckBlock); - } - - private List> CollectAndDesugarYields(Implementation impl, - Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar, Dictionary ogOldGlobalMap) - { - // Collect the yield predicates and desugar yields - List> yields = new List>(); - List cmds = new List(); - foreach (Block b in impl.Blocks) - { - YieldCmd yieldCmd = null; - List newCmds = new List(); - for (int i = 0; i < b.Cmds.Count; i++) - { - Cmd cmd = b.Cmds[i]; - if (cmd is YieldCmd) - { - yieldCmd = (YieldCmd)cmd; - continue; - } - if (yieldCmd != null) - { - PredicateCmd pcmd = cmd as PredicateCmd; - if (pcmd == null) - { - DesugarYield(yieldCmd, cmds, newCmds, ogOldGlobalMap, domainNameToInputVar, domainNameToLocalVar); - if (cmds.Count > 0) - { - yields.Add(cmds); - cmds = new List(); - } - yieldCmd = null; - } - else - { - cmds.Add(pcmd); - } - } - - if (cmd is CallCmd) - { - CallCmd callCmd = cmd as CallCmd; - if (yieldingProcs.Contains(callCmd.Proc)) - { - AddCallToYieldProc(callCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); - } - if (callCmd.IsAsync) - { - if (!asyncAndParallelCallDesugarings.ContainsKey(callCmd.Proc.Name)) - { - asyncAndParallelCallDesugarings[callCmd.Proc.Name] = new Procedure(Token.NoToken, string.Format("DummyAsyncTarget_{0}", callCmd.Proc.Name), callCmd.Proc.TypeParameters, callCmd.Proc.InParams, callCmd.Proc.OutParams, callCmd.Proc.Requires, new List(), new List()); - } - var dummyAsyncTargetProc = asyncAndParallelCallDesugarings[callCmd.Proc.Name]; - CallCmd dummyCallCmd = new CallCmd(callCmd.tok, dummyAsyncTargetProc.Name, callCmd.Ins, callCmd.Outs, callCmd.Attributes); - dummyCallCmd.Proc = dummyAsyncTargetProc; - newCmds.Add(dummyCallCmd); - } - else - { - newCmds.Add(callCmd); - } - if (yieldingProcs.Contains(callCmd.Proc)) - { - HashSet availableLinearVars = new HashSet(AvailableLinearVars(callCmd)); - linearTypeChecker.AddAvailableVars(callCmd, availableLinearVars); - - if (!callCmd.IsAsync && globalMods.Count > 0 && pc != null) - { - // assume pc || alpha(i, g); - Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); - assumeExpr.Type = Type.Bool; - newCmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); - } - - Dictionary domainNameToExpr = ComputeAvailableExprs(availableLinearVars, domainNameToInputVar); - AddUpdatesToOldGlobalVars(newCmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); - } - } - else if (cmd is ParCallCmd) - { - ParCallCmd parCallCmd = cmd as ParCallCmd; - AddCallToYieldProc(parCallCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); - DesugarParallelCallCmd(newCmds, parCallCmd); - HashSet availableLinearVars = new HashSet(AvailableLinearVars(parCallCmd)); - linearTypeChecker.AddAvailableVars(parCallCmd, availableLinearVars); - - if (globalMods.Count > 0 && pc != null) - { - // assume pc || alpha(i, g); - Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); - assumeExpr.Type = Type.Bool; - newCmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); - } - - Dictionary domainNameToExpr = ComputeAvailableExprs(availableLinearVars, domainNameToInputVar); - AddUpdatesToOldGlobalVars(newCmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); - } - else - { - newCmds.Add(cmd); - } - } - if (yieldCmd != null) - { - DesugarYield(yieldCmd, cmds, newCmds, ogOldGlobalMap, domainNameToInputVar, domainNameToLocalVar); - if (cmds.Count > 0) - { - yields.Add(cmds); - cmds = new List(); - } - } - if (b.TransferCmd is ReturnCmd) - { - AddCallToYieldProc(b.TransferCmd.tok, newCmds, ogOldGlobalMap, domainNameToLocalVar); - if (pc != null) - { - AssertCmd assertCmd = new AssertCmd(b.TransferCmd.tok, Expr.Ident(ok)); - assertCmd.ErrorData = "Failed to execute atomic action before procedure return"; - newCmds.Add(assertCmd); - } - } - b.Cmds = newCmds; - } - return yields; - } - - private void ProcessLoopHeaders(Implementation impl, Graph graph, HashSet yieldingHeaders, - Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar, Dictionary ogOldGlobalMap, - out List oldPcs, out List oldOks) - { - oldPcs = new List(); - oldOks = new List(); - foreach (Block header in yieldingHeaders) - { - LocalVariable oldPc = null; - LocalVariable oldOk = null; - if (pc != null) - { - oldPc = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}", pc.Name, header.Label), Type.Bool)); - oldPcs.Add(oldPc); - oldOk = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}", ok.Name, header.Label), Type.Bool)); - oldOks.Add(oldOk); - } - Dictionary domainNameToExpr = ComputeAvailableExprs(AvailableLinearVars(header), domainNameToInputVar); - foreach (Block pred in header.Predecessors) - { - AddCallToYieldProc(header.tok, pred.Cmds, ogOldGlobalMap, domainNameToLocalVar); - if (pc != null && !graph.BackEdgeNodes(header).Contains(pred)) - { - pred.Cmds.Add(new AssignCmd(Token.NoToken, new List( - new AssignLhs[] { new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldPc)), new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldOk)) }), - new List(new Expr[] { Expr.Ident(pc), Expr.Ident(ok) }))); - } - AddUpdatesToOldGlobalVars(pred.Cmds, ogOldGlobalMap, domainNameToLocalVar, domainNameToExpr); - } - List newCmds = new List(); - if (pc != null) - { - AssertCmd assertCmd; - assertCmd = new AssertCmd(header.tok, Expr.Eq(Expr.Ident(oldPc), Expr.Ident(pc))); - assertCmd.ErrorData = "Specification state must not change for transitions ending in loop headers"; - newCmds.Add(assertCmd); - assertCmd = new AssertCmd(header.tok, Expr.Imp(Expr.Ident(oldOk), Expr.Ident(ok))); - assertCmd.ErrorData = "Specification state must not change for transitions ending in loop headers"; - newCmds.Add(assertCmd); - } - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - newCmds.Add(new AssumeCmd(Token.NoToken, Expr.Eq(Expr.Ident(domainNameToLocalVar[domainName]), domainNameToExpr[domainName]))); - } - foreach (Variable v in ogOldGlobalMap.Keys) - { - newCmds.Add(new AssumeCmd(Token.NoToken, Expr.Eq(Expr.Ident(v), Expr.Ident(ogOldGlobalMap[v])))); - } - newCmds.AddRange(header.Cmds); - header.Cmds = newCmds; - } - } - - private void AddInitialBlock(Implementation impl, List oldPcs, List oldOks, - Dictionary domainNameToInputVar, Dictionary domainNameToLocalVar, Dictionary ogOldGlobalMap) - { - // Add initial block - List lhss = new List(); - List rhss = new List(); - if (pc != null) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(pc))); - rhss.Add(Expr.False); - foreach (Variable oldPc in oldPcs) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldPc))); - rhss.Add(Expr.False); - } - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ok))); - rhss.Add(Expr.False); - foreach (Variable oldOk in oldOks) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldOk))); - rhss.Add(Expr.False); - } - } - Dictionary domainNameToExpr = new Dictionary(); - foreach (var domainName in linearTypeChecker.linearDomains.Keys) - { - domainNameToExpr[domainName] = Expr.Ident(domainNameToInputVar[domainName]); - } - for (int i = 0; i < impl.InParams.Count - linearTypeChecker.linearDomains.Count; i++) - { - Variable v = impl.InParams[i]; - var domainName = linearTypeChecker.FindDomainName(v); - if (domainName == null) continue; - if (!linearTypeChecker.linearDomains.ContainsKey(domainName)) continue; - var domain = linearTypeChecker.linearDomains[domainName]; - if (!domain.collectors.ContainsKey(v.TypedIdent.Type)) continue; - Expr ie = new NAryExpr(Token.NoToken, new FunctionCall(domain.collectors[v.TypedIdent.Type]), new List { Expr.Ident(v) }); - domainNameToExpr[domainName] = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapOrBool), new List { ie, domainNameToExpr[domainName] }); - } - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(domainNameToLocalVar[domainName]))); - rhss.Add(domainNameToExpr[domainName]); - } - foreach (Variable g in ogOldGlobalMap.Keys) - { - lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ogOldGlobalMap[g]))); - rhss.Add(Expr.Ident(g)); - } - if (lhss.Count > 0) - { - Block initBlock = new Block(Token.NoToken, "og_init", new List { new AssignCmd(Token.NoToken, lhss, rhss) }, new GotoCmd(Token.NoToken, new List { impl.Blocks[0].Label }, new List { impl.Blocks[0] })); - impl.Blocks.Insert(0, initBlock); - } - } - - private void AddYieldProcAndImpl(List decls) - { - if (yieldProc == null) return; - - Program program = linearTypeChecker.program; - List inputs = new List(); - foreach (string domainName in linearTypeChecker.linearDomains.Keys) - { - var domain = linearTypeChecker.linearDomains[domainName]; - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_in", new MapType(Token.NoToken, new List(), new List { domain.elementType }, Type.Bool)), true); - inputs.Add(f); - } - foreach (IdentifierExpr ie in globalMods) - { - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("og_global_old_{0}", ie.Decl.Name), ie.Decl.TypedIdent.Type), true); - inputs.Add(f); - } - List blocks = new List(); - TransferCmd transferCmd = new ReturnCmd(Token.NoToken); - if (yieldCheckerProcs.Count > 0) - { - List blockTargets = new List(); - List labelTargets = new List(); - int labelCount = 0; - foreach (Procedure proc in yieldCheckerProcs) - { - List exprSeq = new List(); - foreach (Variable v in inputs) - { - exprSeq.Add(Expr.Ident(v)); - } - CallCmd callCmd = new CallCmd(Token.NoToken, proc.Name, exprSeq, new List()); - callCmd.Proc = proc; - string label = string.Format("L_{0}", labelCount++); - Block block = new Block(Token.NoToken, label, new List { callCmd }, new ReturnCmd(Token.NoToken)); - labelTargets.Add(label); - blockTargets.Add(block); - blocks.Add(block); - } - transferCmd = new GotoCmd(Token.NoToken, labelTargets, blockTargets); - } - blocks.Insert(0, new Block(Token.NoToken, "enter", new List(), transferCmd)); - - var yieldImpl = new Implementation(Token.NoToken, yieldProc.Name, new List(), inputs, new List(), new List(), blocks); - yieldImpl.Proc = yieldProc; - yieldImpl.AddAttribute("inline", new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); - decls.Add(yieldProc); - decls.Add(yieldImpl); - } - - public static QKeyValue RemoveYieldsAttribute(QKeyValue iter) - { - if (iter == null) return null; - iter.Next = RemoveYieldsAttribute(iter.Next); - return (iter.Key == "yields") ? iter.Next : iter; - } - - public static QKeyValue RemoveMoverAttribute(QKeyValue iter) - { - if (iter == null) return null; - iter.Next = RemoveMoverAttribute(iter.Next); - if (iter.Key == "atomic" || iter.Key == "right" || iter.Key == "left" || iter.Key == "both") - return iter.Next; - else - return iter; - } - - private List Collect() - { - List decls = new List(); - foreach (Procedure proc in yieldCheckerProcs) - { - decls.Add(proc); - } - foreach (Implementation impl in yieldCheckerImpls) - { - decls.Add(impl); - } - foreach (Procedure proc in asyncAndParallelCallDesugarings.Values) - { - decls.Add(proc); - } - AddYieldProcAndImpl(decls); - return decls; - } - - public static void AddCheckers(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) - { - Program program = linearTypeChecker.program; - foreach (int layerNum in civlTypeChecker.AllLayerNums) - { - if (CommandLineOptions.Clo.TrustLayersDownto <= layerNum || layerNum <= CommandLineOptions.Clo.TrustLayersUpto) continue; - - MyDuplicator duplicator = new MyDuplicator(civlTypeChecker, layerNum); - foreach (var proc in program.Procedures) - { - if (!civlTypeChecker.procToActionInfo.ContainsKey(proc)) continue; - Procedure duplicateProc = duplicator.VisitProcedure(proc); - decls.Add(duplicateProc); - } - decls.AddRange(duplicator.impls); - CivlRefinement civlTransform = new CivlRefinement(linearTypeChecker, civlTypeChecker, duplicator); - foreach (var impl in program.Implementations) - { - if (!civlTypeChecker.procToActionInfo.ContainsKey(impl.Proc) || civlTypeChecker.procToActionInfo[impl.Proc].createdAtLayerNum < layerNum) - continue; - Implementation duplicateImpl = duplicator.VisitImplementation(impl); - civlTransform.TransformImpl(duplicateImpl); - decls.Add(duplicateImpl); - } - decls.AddRange(civlTransform.Collect()); - } - } - } -} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/CivlTypeChecker.cs boogie-2.4.1+dfsg/Source/Concurrency/CivlTypeChecker.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/CivlTypeChecker.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/CivlTypeChecker.cs 2019-10-25 18:17:05.000000000 +0000 @@ -1,190 +1,138 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using Microsoft.Boogie; using System.Diagnostics.Contracts; using System.Diagnostics; +using Microsoft.Boogie.GraphUtil; namespace Microsoft.Boogie { public enum MoverType { - Top, Atomic, Right, Left, Both } - public class ActionInfo + public class AtomicAction { public Procedure proc; - public int createdAtLayerNum; - public int availableUptoLayerNum; - public bool hasImplementation; - public bool isExtern; + public Implementation impl; + public MoverType moverType; + public LayerRange layerRange; - public ActionInfo(Procedure proc, int createdAtLayerNum, int availableUptoLayerNum) + public Dictionary layerToActionCopy; + public int asyncFreeLayer; + + public AtomicAction(Procedure proc, Implementation impl, MoverType moverType, LayerRange layerRange) { this.proc = proc; - this.createdAtLayerNum = createdAtLayerNum; - this.availableUptoLayerNum = availableUptoLayerNum; - this.hasImplementation = false; - this.isExtern = QKeyValue.FindBoolAttribute(proc.Attributes, "extern"); - } + this.impl = impl; + this.moverType = moverType; + this.layerRange = layerRange; - public virtual bool IsRightMover - { - get { return true; } - } + this.layerToActionCopy = new Dictionary(); - public virtual bool IsLeftMover - { - get { return true; } + // We usually declare the Boogie procedure and implementation of an atomic action together. + // Since Boogie only stores the supplied attributes (in particular linearity) in the procedure parameters, + // we copy them into the implementation parameters here. + for (int i = 0; i < proc.InParams.Count; i++) + { + impl.InParams[i].Attributes = proc.InParams[i].Attributes; + } + for (int i = 0; i < proc.OutParams.Count; i++) + { + impl.OutParams[i].Attributes = proc.OutParams[i].Attributes; + } } + + // TODO: Check usage! + public bool IsRightMover { get { return moverType == MoverType.Right || moverType == MoverType.Both; } } + public bool IsLeftMover { get { return moverType == MoverType.Left || moverType == MoverType.Both; } } } - public class AtomicActionInfo : ActionInfo + public class AtomicActionCopy { - public Ensures ensures; - public MoverType moverType; + public Procedure proc; + public Implementation impl; + + // Strictly speaking the gate is the same on every layer, but for easier parameter substitution + // we keep the per-layer version of gates. public List gate; - public CodeExpr action; - public List thisGate; - public CodeExpr thisAction; - public List thisInParams; - public List thisOutParams; - public List thatGate; - public CodeExpr thatAction; - public List thatInParams; - public List thatOutParams; + + public List firstGate; + public CodeExpr firstAction; + public List firstInParams; + public List firstOutParams; + public Dictionary firstMap; + + public List secondGate; + public CodeExpr secondAction; + public List secondInParams; + public List secondOutParams; + public Dictionary secondMap; + + public HashSet gateUsedGlobalVars; public HashSet actionUsedGlobalVars; public HashSet modifiedGlobalVars; - public HashSet gateUsedGlobalVars; - public bool hasAssumeCmd; - public Dictionary thisMap; - public Dictionary thatMap; - public bool CommutesWith(AtomicActionInfo actionInfo) + public AtomicActionCopy(Procedure proc, Implementation impl) { - if (this.modifiedGlobalVars.Intersect(actionInfo.actionUsedGlobalVars).Count() > 0) - return false; - if (this.actionUsedGlobalVars.Intersect(actionInfo.modifiedGlobalVars).Count() > 0) - return false; - return true; - } + this.proc = proc; + this.impl = impl; - public override bool IsRightMover - { - get { return moverType == MoverType.Right || moverType == MoverType.Both; } + // The gate of an atomic action is represented as asserts at the beginning of the procedure body. + this.gate = impl.Blocks[0].cmds.TakeWhile((c, i) => c is AssertCmd).Cast().ToList(); + impl.Blocks[0].cmds.RemoveRange(0, gate.Count); } - public override bool IsLeftMover + public void Setup() { - get { return moverType == MoverType.Left || moverType == MoverType.Both; } + SetupCopy(ref firstGate, ref firstAction, ref firstInParams, ref firstOutParams, ref firstMap, "first_"); + SetupCopy(ref secondGate, ref secondAction, ref secondInParams, ref secondOutParams, ref secondMap, "second_"); + + gateUsedGlobalVars = new HashSet(VariableCollector.Collect(gate).Where(x => x is GlobalVariable)); + actionUsedGlobalVars = new HashSet(VariableCollector.Collect(impl).Where(x => x is GlobalVariable)); + modifiedGlobalVars = new HashSet(AssignedVariables().Where(x => x is GlobalVariable)); } - public AtomicActionInfo(Procedure proc, Ensures ensures, MoverType moverType, int layerNum, int availableUptoLayerNum) - : base(proc, layerNum, availableUptoLayerNum) + private void SetupCopy(ref List gateCopy, ref CodeExpr actionCopy, ref List inParamsCopy, ref List outParamsCopy, ref Dictionary varMap, string prefix) { - this.ensures = ensures; - this.moverType = moverType; - this.gate = new List(); - this.action = ensures.Condition as CodeExpr; - this.thisGate = new List(); - this.thisInParams = new List(); - this.thisOutParams = new List(); - this.thatGate = new List(); - this.thatInParams = new List(); - this.thatOutParams = new List(); - this.hasAssumeCmd = false; - this.thisMap = new Dictionary(); - this.thatMap = new Dictionary(); - - foreach (Block block in this.action.Blocks) - { - block.Cmds.ForEach(x => this.hasAssumeCmd = this.hasAssumeCmd || x is AssumeCmd); - } - - foreach (Block block in this.action.Blocks) - { - if (block.TransferCmd is ReturnExprCmd) - { - block.TransferCmd = new ReturnCmd(block.TransferCmd.tok); - } - } - - var cmds = this.action.Blocks[0].Cmds; - for (int i = 0; i < cmds.Count; i++) - { - AssertCmd assertCmd = cmds[i] as AssertCmd; - if (assertCmd == null) break; - this.gate.Add(assertCmd); - cmds[i] = new AssumeCmd(assertCmd.tok, Expr.True); - } + gateCopy = new List(); + inParamsCopy = new List(); + outParamsCopy = new List(); + varMap = new Dictionary(); - foreach (Variable x in proc.InParams) + foreach (Variable x in impl.InParams) { - Variable thisx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "this_" + x.Name, x.TypedIdent.Type), true, x.Attributes); - this.thisInParams.Add(thisx); - this.thisMap[x] = Expr.Ident(thisx); - Variable thatx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "that_" + x.Name, x.TypedIdent.Type), true, x.Attributes); - this.thatInParams.Add(thatx); - this.thatMap[x] = Expr.Ident(thatx); + Variable xCopy = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, prefix + x.Name, x.TypedIdent.Type), true, x.Attributes); + inParamsCopy.Add(xCopy); + varMap[x] = Expr.Ident(xCopy); } - foreach (Variable x in proc.OutParams) + foreach (Variable x in impl.OutParams) { - Variable thisx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "this_" + x.Name, x.TypedIdent.Type), false, x.Attributes); - this.thisOutParams.Add(thisx); - this.thisMap[x] = Expr.Ident(thisx); - Variable thatx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "that_" + x.Name, x.TypedIdent.Type), false, x.Attributes); - this.thatOutParams.Add(thatx); - this.thatMap[x] = Expr.Ident(thatx); + Variable xCopy = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, prefix + x.Name, x.TypedIdent.Type), false, x.Attributes); + outParamsCopy.Add(xCopy); + varMap[x] = Expr.Ident(xCopy); } - List thisLocVars = new List(); - List thatLocVars = new List(); - foreach (Variable x in this.action.LocVars) + List localsCopy = new List(); + foreach (Variable x in impl.LocVars) { - Variable thisx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "this_" + x.Name, x.TypedIdent.Type), false); - thisMap[x] = Expr.Ident(thisx); - thisLocVars.Add(thisx); - Variable thatx = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "that_" + x.Name, x.TypedIdent.Type), false); - thatMap[x] = Expr.Ident(thatx); - thatLocVars.Add(thatx); + Variable xCopy = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, prefix + x.Name, x.TypedIdent.Type), false); + varMap[x] = Expr.Ident(xCopy); + localsCopy.Add(xCopy); } Contract.Assume(proc.TypeParameters.Count == 0); - Substitution thisSubst = Substituter.SubstitutionFromHashtable(this.thisMap); - Substitution thatSubst = Substituter.SubstitutionFromHashtable(this.thatMap); - foreach (AssertCmd assertCmd in this.gate) - { - this.thisGate.Add((AssertCmd)Substituter.Apply(thisSubst, assertCmd)); - this.thatGate.Add((AssertCmd)Substituter.Apply(thatSubst, assertCmd)); - } - this.thisAction = new CodeExpr(thisLocVars, SubstituteBlocks(this.action.Blocks, thisSubst, "this_")); - this.thatAction = new CodeExpr(thatLocVars, SubstituteBlocks(this.action.Blocks, thatSubst, "that_")); - + AtomicActionDuplicator aad = new AtomicActionDuplicator(prefix, varMap); + foreach (AssertCmd assertCmd in gate) { - VariableCollector collector = new VariableCollector(); - collector.Visit(this.action); - this.actionUsedGlobalVars = new HashSet(collector.usedVars.Where(x => x is GlobalVariable)); - } - - List modifiedVars = new List(); - foreach (Block block in this.action.Blocks) - { - block.Cmds.ForEach(cmd => cmd.AddAssignedVariables(modifiedVars)); - } - this.modifiedGlobalVars = new HashSet(modifiedVars.Where(x => x is GlobalVariable)); - - { - VariableCollector collector = new VariableCollector(); - this.gate.ForEach(assertCmd => collector.Visit(assertCmd)); - this.gateUsedGlobalVars = new HashSet(collector.usedVars.Where(x => x is GlobalVariable)); + gateCopy.Add((AssertCmd)aad.Visit(assertCmd)); } + actionCopy = new CodeExpr(localsCopy, SubstituteBlocks(impl.Blocks, aad)); } - private List SubstituteBlocks(List blocks, Substitution subst, string blockLabelPrefix) + private List SubstituteBlocks(List blocks, AtomicActionDuplicator aad) { Dictionary blockMap = new Dictionary(); List otherBlocks = new List(); @@ -193,11 +141,12 @@ List otherCmds = new List(); foreach (Cmd cmd in block.Cmds) { - otherCmds.Add(Substituter.Apply(subst, cmd)); + otherCmds.Add((Cmd)aad.Visit(cmd)); } Block otherBlock = new Block(); + otherBlock.tok = block.tok; otherBlock.Cmds = otherCmds; - otherBlock.Label = blockLabelPrefix + block.Label; + otherBlock.Label = aad.prefix + block.Label; otherBlocks.Add(otherBlock); blockMap[block] = otherBlock; } @@ -220,63 +169,152 @@ } return otherBlocks; } - } - public class SharedVariableInfo - { - public int introLayerNum; - public int hideLayerNum; + private List AssignedVariables() + { + List modifiedVars = new List(); + foreach (Cmd cmd in impl.Blocks.SelectMany(b => b.Cmds)) + { + cmd.AddAssignedVariables(modifiedVars); + } + return modifiedVars; + } - public SharedVariableInfo(int introLayerNum, int hideLayerNum) + public bool HasAssumeCmd { get { return impl.Blocks.Any(b => b.Cmds.Any(c => c is AssumeCmd)); } } + + public bool TriviallyCommutesWith(AtomicActionCopy other) { - this.introLayerNum = introLayerNum; - this.hideLayerNum = hideLayerNum; + return this.modifiedGlobalVars.Intersect(other.actionUsedGlobalVars).Count() == 0 && + this.actionUsedGlobalVars.Intersect(other.modifiedGlobalVars).Count() == 0; } } - public class LayerEraser : ReadOnlyVisitor + /// + /// Renames variables (first_ and second_ prefix) in atomic action copies. + /// We do not use standard substitution, because we also need to rename bound variables + /// (because of potential substitution in commutativity checkers). + /// A substitution for regular variables is supplied from the outside, replacements for + /// bound variables are created on the fly. + /// + class AtomicActionDuplicator : Duplicator { - private QKeyValue RemoveLayerAttribute(QKeyValue iter) + public string prefix; + public Dictionary subst; + private Dictionary bound; + + public AtomicActionDuplicator(string prefix, Dictionary subst) { + this.prefix = prefix; + this.subst = subst; + this.bound = new Dictionary(); + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) { - if (iter == null) return null; - iter.Next = RemoveLayerAttribute(iter.Next); - return (iter.Key == "layer") ? iter.Next : iter; + if (subst.ContainsKey(node.Decl)) + { + return subst[node.Decl]; + } + else if (bound.ContainsKey(node.Decl)) + { + return bound[node.Decl]; + } + return base.VisitIdentifierExpr(node); } - public override Variable VisitVariable(Variable node) + public override BinderExpr VisitBinderExpr(BinderExpr node) { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitVariable(node); + var oldToNew = node.Dummies.ToDictionary(x => x, x => new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, prefix + x.Name, x.TypedIdent.Type), x.Attributes)); + + foreach (var x in node.Dummies) + { + bound.Add(x, Expr.Ident(oldToNew[x])); + } + + BinderExpr expr = base.VisitBinderExpr(node); + expr.Dummies = node.Dummies.Select(x => oldToNew[x]).ToList(); + + // We process triggers of quantifer expressions here, because otherwise the + // substitutions for bound variables have to be leaked outside this procedure. + if (node is QuantifierExpr quantifierExpr) + { + if (quantifierExpr.Triggers != null) + { + ((QuantifierExpr)expr).Triggers = this.VisitTrigger(quantifierExpr.Triggers); + } + } + + foreach (var x in node.Dummies) + { + bound.Remove(x); + } + + return expr; } - public override Procedure VisitProcedure(Procedure node) + public override QuantifierExpr VisitQuantifierExpr(QuantifierExpr node) { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitProcedure(node); + // Don't remove this implementation! Triggers should be duplicated in VisitBinderExpr. + return (QuantifierExpr)this.VisitBinderExpr(node); } + } + + public abstract class YieldingProc + { + public Procedure proc; + public MoverType moverType; + public int upperLayer; - public override Implementation VisitImplementation(Implementation node) + public YieldingProc(Procedure proc, MoverType moverType, int upperLayer) { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitImplementation(node); + this.proc = proc; + this.moverType = moverType; + this.upperLayer = upperLayer; } - public override Requires VisitRequires(Requires node) + public bool IsRightMover { get { return moverType == MoverType.Right || moverType == MoverType.Both; } } + public bool IsLeftMover { get { return moverType == MoverType.Left || moverType == MoverType.Both; } } + } + + public class SkipProc : YieldingProc + { + public SkipProc(Procedure proc, int upperLayer) + : base(proc, MoverType.Both, upperLayer) { } + } + + public class MoverProc : YieldingProc + { + public HashSet modifiedGlobalVars; + + public MoverProc(Procedure proc, MoverType moverType, int upperLayer) + : base(proc, moverType, upperLayer) { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitRequires(node); + modifiedGlobalVars = new HashSet(proc.Modifies.Select(ie => ie.Decl)); } + } - public override Ensures VisitEnsures(Ensures node) + public class ActionProc : YieldingProc + { + public AtomicAction refinedAction; + public Procedure addPendingAsyncProc; + + public ActionProc(Procedure proc, AtomicAction refinedAction, int upperLayer) + : base(proc, refinedAction.moverType, upperLayer) { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitEnsures(node); + this.refinedAction = refinedAction; } + } + + public class IntroductionProc + { + public Procedure proc; + public LayerRange layerRange; + public bool isLeaky; - public override Cmd VisitAssertCmd(AssertCmd node) + public IntroductionProc(Procedure proc, LayerRange layerRange, bool isLeaking) { - node.Attributes = RemoveLayerAttribute(node.Attributes); - return base.VisitAssertCmd(node); + this.proc = proc; + this.isLeaky = isLeaking; + this.layerRange = layerRange; } } @@ -284,458 +322,672 @@ { public int lowerLayerNum; public int upperLayerNum; - public LayerRange(int layer) - { - this.lowerLayerNum = layer; - this.upperLayerNum = layer; - } + + public static LayerRange MinMax = new LayerRange(int.MinValue, int.MaxValue); + + public LayerRange(int layer) : this(layer, layer) { } + public LayerRange(int lower, int upper) { + Debug.Assert(lower <= upper); this.lowerLayerNum = lower; this.upperLayerNum = upper; } - public LayerRange(IEnumerable layerNums) - { - int min = int.MaxValue; - int max = int.MinValue; - foreach (var layerNum in layerNums) - { - if (layerNum < min) - { - min = layerNum; - } - if (max < layerNum) - { - max = layerNum; - } - } - this.lowerLayerNum = min; - this.upperLayerNum = max; - } + public bool Contains(int layerNum) { return lowerLayerNum <= layerNum && layerNum <= upperLayerNum; } - public bool Subset(int lower, int upper) - { - return lower <= lowerLayerNum && upperLayerNum <= upper; - } - public bool Equal(int lower, int upper) - { - return lower == lowerLayerNum && upperLayerNum == upper; - } - public bool Subset(LayerRange info) - { - return info.lowerLayerNum <= lowerLayerNum && upperLayerNum <= info.upperLayerNum; - } - } - public class AtomicProcedureInfo - { - public bool isPure; - public LayerRange layerRange; - public AtomicProcedureInfo() + public bool Subset(LayerRange other) { - this.isPure = true; - this.layerRange = null; - } - public AtomicProcedureInfo(LayerRange layerRange) - { - this.isPure = false; - this.layerRange = layerRange; + return other.lowerLayerNum <= lowerLayerNum && upperLayerNum <= other.upperLayerNum; } } - public class LocalVariableInfo + public static class AttributeQueryExtensionMethods { - public int layer; - public LocalVariableInfo(int layer) - { - this.layer = layer; - } + public static bool HasAttribute(this ICarriesAttributes obj, string attribute) + { return QKeyValue.FindBoolAttribute(obj.Attributes, attribute); } + + public static bool IsPure (this Declaration decl) { return decl.HasAttribute(CivlAttributes.PURE); } + public static bool IsYield(this Declaration decl) { return decl.HasAttribute(CivlAttributes.YIELDS); } + + public static bool IsExtern(this Declaration decl) { return decl.HasAttribute("extern"); } } - public class CivlTypeChecker : ReadOnlyVisitor + public class CivlTypeChecker { - CheckingContext checkingContext; - Procedure enclosingProc; - Implementation enclosingImpl; - HashSet sharedVarsAccessed; - int introducedLocalVarsUpperBound; - + public CheckingContext checkingContext; public Program program; - public int errorCount; - public Dictionary globalVarToSharedVarInfo; - public Dictionary procToActionInfo; - public Dictionary procToAtomicProcedureInfo; + + // Don't access directly! + // TypeCheckLocalVariables initializes globalVarToLayerRange such that variables with min-max layer range are not explicitly stored. + // Similarly for localVarToIntroLayer / TypeCheckLocalVariables. + // Use public access methods. + private Dictionary globalVarToLayerRange; + private Dictionary localVarToIntroLayer; + + public Dictionary procToAtomicAction; + public Dictionary procToYieldingProc; + public Dictionary procToIntroductionProc; + public Dictionary, + List> atomicActionPairToWitnessFunctions; + public Dictionary> absyToLayerNums; - public Dictionary localVarToLocalVariableInfo; - Dictionary pureCallLayer; + Dictionary introductionCallToLayer; + + public GlobalVariable pendingAsyncMultiset; + + // This collections are for convenience in later phases and are only initialized at the end of type checking. + public List allRefinementLayers; + public List allAtomicActionLayers; + public List sharedVariables; + public List sharedVariableIdentifiers; + + public CivlTypeChecker(Program program) + { + this.checkingContext = new CheckingContext(null); + this.program = program; + + this.globalVarToLayerRange = new Dictionary(); + this.localVarToIntroLayer = new Dictionary(); + this.absyToLayerNums = new Dictionary>(); + this.procToAtomicAction = new Dictionary(); + this.procToYieldingProc = new Dictionary(); + this.procToIntroductionProc = new Dictionary(); + this.introductionCallToLayer = new Dictionary(); + this.atomicActionPairToWitnessFunctions = new Dictionary, + List>(); + } public bool CallExists(CallCmd callCmd, int enclosingProcLayerNum, int layerNum) { - Debug.Assert(procToAtomicProcedureInfo.ContainsKey(callCmd.Proc)); - var atomicProcedureInfo = procToAtomicProcedureInfo[callCmd.Proc]; - if (atomicProcedureInfo.isPure) + Debug.Assert(procToIntroductionProc.ContainsKey(callCmd.Proc)); + var introductionProc = procToIntroductionProc[callCmd.Proc]; + if (introductionProc.isLeaky) { - return pureCallLayer[callCmd] <= layerNum; + return enclosingProcLayerNum == layerNum; } else { - return enclosingProcLayerNum == layerNum; + return introductionCallToLayer[callCmd] <= layerNum; } } - private static List FindLayers(QKeyValue kv) + private List FindLayers(QKeyValue kv) { List layers = new List(); for (; kv != null; kv = kv.Next) { - if (kv.Key != "layer") continue; + if (kv.Key != CivlAttributes.LAYER) continue; foreach (var o in kv.Params) { - Expr e = o as Expr; - if (e == null) return null; - LiteralExpr l = e as LiteralExpr; - if (l == null) return null; - if (!l.isBigNum) return null; - layers.Add(l.asBigNum.ToIntSafe); + if (o is LiteralExpr l && l.isBigNum) + { + layers.Add(l.asBigNum.ToIntSafe); + } + else + { + checkingContext.Error(kv, "Layer has to be an integer."); + } } } return layers; } - private static int Least(IEnumerable layerNums) + private LayerRange ToLayerRange(List layerNums, Absy absy) { - int least = int.MaxValue; - foreach (var layer in layerNums) + // We return min-max range for invalid declarations in order to proceed with type checking. + if (layerNums.Count == 0) + { + return LayerRange.MinMax; + } + else if (layerNums.Count == 1) + { + return new LayerRange(layerNums[0], layerNums[0]); + } + else if (layerNums.Count == 2) { - if (layer < least) + if (layerNums[0] <= layerNums[1]) + { + return new LayerRange(layerNums[0], layerNums[1]); + } + else { - least = layer; + Error(absy, "Invalid layer range"); + return LayerRange.MinMax; } } - return least; + else + { + Error(absy, "Invalid layer range"); + return LayerRange.MinMax; + } } - private static MoverType GetMoverType(Ensures e) + /// Parses attributes for mover type declarations. + /// Returns the first mover type found (or null if none is found) and issues warnings if multiple mover types are found. + private MoverType? GetMoverType(QKeyValue kv) { - if (QKeyValue.FindBoolAttribute(e.Attributes, "atomic")) - return MoverType.Atomic; - if (QKeyValue.FindBoolAttribute(e.Attributes, "right")) - return MoverType.Right; - if (QKeyValue.FindBoolAttribute(e.Attributes, "left")) - return MoverType.Left; - if (QKeyValue.FindBoolAttribute(e.Attributes, "both")) - return MoverType.Both; - return MoverType.Top; + MoverType? moverType = null; + + for (; kv != null; kv = kv.Next) + { + if (kv.Params.Count == 0) + { + MoverType? x = null; + if (kv.Key == CivlAttributes.ATOMIC) + x = MoverType.Atomic; + else if (kv.Key == CivlAttributes.RIGHT) + x = MoverType.Right; + else if (kv.Key == CivlAttributes.LEFT) + x = MoverType.Left; + else if (kv.Key == CivlAttributes.BOTH) + x = MoverType.Both; + + if (x.HasValue) + { + if (moverType.HasValue) + checkingContext.Warning(kv, "Ignoring duplicate mover type declaration ({0}).", kv.Key); + else + moverType = x; + } + } + } + + return moverType; } - public CivlTypeChecker(Program program) + public void TypeCheck() { - this.errorCount = 0; - this.checkingContext = new CheckingContext(null); - this.program = program; - this.enclosingProc = null; - this.enclosingImpl = null; - this.sharedVarsAccessed = null; - this.introducedLocalVarsUpperBound = int.MinValue; + // TODO: eliminate early returns + // Can we make later phases resilient to errors in previous phases, + // such that as much feedback as possible is generated for the user? + // For example, by creating "invalid" objects like an ActionProc with + // null refinedAction in TypeCheckYieldingProcedureDecls, such that + // later checks can work with them but also do not crash. - this.localVarToLocalVariableInfo = new Dictionary(); - this.absyToLayerNums = new Dictionary>(); - this.globalVarToSharedVarInfo = new Dictionary(); - this.procToActionInfo = new Dictionary(); - this.procToAtomicProcedureInfo = new Dictionary(); - this.pureCallLayer = new Dictionary(); - - foreach (var g in program.GlobalVariables) + TypeCheckGlobalVariables(); + TypeCheckIntroductionProcedures(); + + TypeCheckAtomicActionDecls(); + TypeCheckYieldingProcedureDecls(); + + TypeCheckLocalVariables(); + + TypeCheckAtomicActionImpls(); + + if (checkingContext.ErrorCount != 0) + return; + + ComputeAsyncFreeLayers(); + + // List of all layers where refinement happens + allRefinementLayers = procToYieldingProc.Values.Select(a => a.upperLayer).OrderBy(l => l).Distinct().ToList(); + + // List of all layers where the set of available atomic actions changes (through availability or refinement) + allAtomicActionLayers = allRefinementLayers.ToList(); + allAtomicActionLayers.AddRange(procToAtomicAction.Values.Select(a => a.layerRange.lowerLayerNum)); + allAtomicActionLayers.AddRange(procToAtomicAction.Values.Select(a => a.layerRange.upperLayerNum)); + allAtomicActionLayers = allAtomicActionLayers.Distinct().OrderBy(l => l).ToList(); + + foreach (var kv in absyToLayerNums) { - List layerNums = FindLayers(g.Attributes); - if (layerNums.Count == 0) - { - // Inaccessible from yielding and atomic procedures - } - else if (layerNums.Count == 1) - { - this.globalVarToSharedVarInfo[g] = new SharedVariableInfo(layerNums[0], int.MaxValue); - } - else if (layerNums.Count == 2) + foreach (var layer in kv.Value) { - this.globalVarToSharedVarInfo[g] = new SharedVariableInfo(layerNums[0], layerNums[1]); + if (!allRefinementLayers.Contains(layer)) + { + checkingContext.Error(kv.Key, "No refinement on layer {0}", layer); + } } - else + } + + CheckAtomicActionAcyclicity(); + + if (checkingContext.ErrorCount != 0) + return; + + AddPendingAsyncMachinery(); + GenerateAtomicActionCopies(); + TypeCheckYieldingProcedureImpls(); + + sharedVariables = program.GlobalVariables.ToList(); + sharedVariableIdentifiers = sharedVariables.Select(v => Expr.Ident(v)).ToList(); + + TypeCheckWitnessFunctions(); + + new AttributeEraser().VisitProgram(program); + } + + public void SubstituteBackwardAssignments() + { + foreach (var action in procToAtomicAction.Values) + { + foreach (var actionCopy in action.layerToActionCopy.Values) { - Error(g, "Too many layer numbers"); + SubstituteBackwardAssignments(actionCopy); } } } - private HashSet allLayerNums; - public IEnumerable AllLayerNums + private void SubstituteBackwardAssignments(AtomicActionCopy action) { - get + foreach (Block block in action.impl.Blocks) { - if (allLayerNums == null) + List cmds = new List(); + foreach (Cmd cmd in block.cmds) { - allLayerNums = new HashSet(); - foreach (ActionInfo actionInfo in procToActionInfo.Values) - { - allLayerNums.Add(actionInfo.createdAtLayerNum); - } - foreach (var layerNums in absyToLayerNums.Values) + if (cmd is AssignCmd _assignCmd && + QKeyValue.FindBoolAttribute(_assignCmd.Attributes, CivlAttributes.BACKWARD)) { - foreach (var layer in layerNums) + AssignCmd assignCmd = _assignCmd.AsSimpleAssignCmd; + var lhss = assignCmd.Lhss; + var rhss = assignCmd.Rhss; + var rhssVars = rhss.SelectMany(x => VariableCollector.Collect(x)); + var lhssVars = lhss.SelectMany(x => VariableCollector.Collect(x)); + if (rhssVars.Intersect(lhssVars).Any()) { - allLayerNums.Add(layer); + // TODO + throw new NotImplementedException("Substitution of backward assignment where lhs appears on rhs"); + } + else + { + List assumeExprs = new List(); + for (int k = 0; k < lhss.Count; k++) + { + assumeExprs.Add(Expr.Eq(lhss[k].AsExpr, rhss[k])); + } + cmds.Add(new AssumeCmd(Token.NoToken, Expr.And(assumeExprs))); + cmds.Add(new HavocCmd(Token.NoToken, lhss.Select(x => x.DeepAssignedIdentifier).ToList())); } } + else + { + cmds.Add(cmd); + } } - return allLayerNums; + block.cmds = cmds; } } - private LayerRange FindLayerRange() + private void TypeCheckWitnessFunctions() { - int maxIntroLayerNum = int.MinValue; - int minHideLayerNum = int.MaxValue; - foreach (var g in sharedVarsAccessed) + WitnessFunctionVisitor wfv = new WitnessFunctionVisitor(this); + foreach (var f in program.Functions) { - if (globalVarToSharedVarInfo[g].introLayerNum > maxIntroLayerNum) - { - maxIntroLayerNum = globalVarToSharedVarInfo[g].introLayerNum; - } - if (globalVarToSharedVarInfo[g].hideLayerNum < minHideLayerNum) + wfv.VisitFunction(f); + } + foreach (var witnessFunction in wfv.allWitnessFunctions) + { + foreach (var layer in witnessFunction.layers) { - minHideLayerNum = globalVarToSharedVarInfo[g].hideLayerNum; + var key = Tuple.Create( + witnessFunction.firstAction.layerToActionCopy[layer], + witnessFunction.secondAction.layerToActionCopy[layer]); + if (!atomicActionPairToWitnessFunctions.ContainsKey(key)) + { + atomicActionPairToWitnessFunctions[key] = new List(); + } + atomicActionPairToWitnessFunctions[key].Add(witnessFunction); } } - return new LayerRange(maxIntroLayerNum, minHideLayerNum); } - public void TypeCheck() + private void TypeCheckGlobalVariables() + { + foreach (var g in program.GlobalVariables) + { + var layerRange = ToLayerRange(FindLayers(g.Attributes), g); + if (layerRange != LayerRange.MinMax) + globalVarToLayerRange[g] = layerRange; + } + } + + public LayerRange GlobalVariableLayerRange(Variable g) { - foreach (var proc in program.Procedures) + if (globalVarToLayerRange.ContainsKey(g)) + return globalVarToLayerRange[g]; + return LayerRange.MinMax; + } + + private void TypeCheckAtomicActionDecls() + { + // Atomic action: + // * no {:yield} + // * mover type + // * layer range + foreach (var proc in program.Procedures.Where(p => !p.IsYield())) { - if (!QKeyValue.FindBoolAttribute(proc.Attributes, "pure")) continue; - if (QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) + MoverType? moverType = GetMoverType(proc.Attributes); + if (!moverType.HasValue) continue; + + LayerRange layerRange = ToLayerRange(FindLayers(proc.Attributes), proc); + + if (proc.Requires.Count + proc.Ensures.Count > 0) + { + Error(proc, "Atomic action can not have preconditions or postconditions"); + } + + var actionImpls = program.Implementations.Where(i => i.Name == proc.Name).ToList(); + if (actionImpls.Count == 0) { - Error(proc, "Pure procedure must not yield"); + Error(proc, "Atomic action specification missing"); continue; } - if (QKeyValue.FindBoolAttribute(proc.Attributes, "layer")) + else if (actionImpls.Count > 1) { - Error(proc, "Pure procedure must not have layers"); + Error(proc, "More then one atomic action specification provided"); continue; } - if (proc.Modifies.Count > 0) + Implementation impl = actionImpls[0]; + + impl.PruneUnreachableBlocks(); + Graph cfg = Program.GraphFromImpl(impl); + if (!Graph.Acyclic(cfg, impl.Blocks[0])) { - Error(proc, "Pure procedure must not modify a global variable"); + Error(proc, "Atomic action specification can not have loops"); continue; } - procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(); - } - foreach (var proc in program.Procedures) - { - if (QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) continue; - var procLayerNums = FindLayers(proc.Attributes); - if (procLayerNums.Count == 0) continue; - foreach (IdentifierExpr ie in proc.Modifies) + + bool inGate = true; + foreach (var cmd in impl.Blocks[0].cmds) { - if (!globalVarToSharedVarInfo.ContainsKey(ie.Decl)) + if (inGate && !(cmd is AssertCmd)) { - Error(proc, "Atomic procedure cannot modify a global variable without layer numbers"); - continue; + inGate = false; } - } - int lower, upper; - if (procLayerNums.Count == 1) - { - lower = procLayerNums[0]; - upper = procLayerNums[0]; - } - else if (procLayerNums.Count == 2) - { - lower = procLayerNums[0]; - upper = procLayerNums[1]; - if (lower >= upper) + else if (!inGate && cmd is AssertCmd) { - Error(proc, "Lower layer must be less than upper layer"); - continue; + Error(cmd, "Assert is only allowed in the gate of an atomic action"); } } - else + foreach (var cmd in impl.Blocks.Skip(1).SelectMany(b => b.cmds).OfType()) { - Error(proc, "Atomic procedure must specify a layer range"); - continue; + Error(cmd, "Assert is only allowed in the gate of an atomic action"); } - LayerRange layerRange = new LayerRange(lower, upper); - procToAtomicProcedureInfo[proc] = new AtomicProcedureInfo(layerRange); + + procToAtomicAction[proc] = new AtomicAction(proc, impl, moverType.Value, layerRange); + } + } + + private void TypeCheckAtomicActionImpls() + { + AtomicActionVisitor atomicActionVisitor = new AtomicActionVisitor(this); + foreach (var action in procToAtomicAction.Values) + { + atomicActionVisitor.VisitAction(action); } - if (errorCount > 0) return; + } - foreach (Implementation impl in program.Implementations) + private void TypeCheckIntroductionProcedures() + { + // Introduction procedure: + // * no {:yield} + // * no mover type + // layer range + foreach (var proc in program.Procedures.Where(proc => !proc.IsYield() && !GetMoverType(proc.Attributes).HasValue)) { - if (!procToAtomicProcedureInfo.ContainsKey(impl.Proc)) continue; - var atomicProcedureInfo = procToAtomicProcedureInfo[impl.Proc]; - if (atomicProcedureInfo.isPure) + LayerRange layerRange = ToLayerRange(FindLayers(proc.Attributes), proc); + bool isLeaky = proc.Modifies.Count + proc.OutParams.Count > 0; + foreach (var ie in proc.Modifies) { - this.enclosingImpl = impl; - (new PurityChecker(this)).VisitImplementation(impl); - } - else - { - this.enclosingImpl = impl; - this.sharedVarsAccessed = new HashSet(); - (new PurityChecker(this)).VisitImplementation(impl); - LayerRange upperBound = FindLayerRange(); - LayerRange lowerBound = atomicProcedureInfo.layerRange; - if (!lowerBound.Subset(upperBound)) + if (GlobalVariableLayerRange(ie.Decl).lowerLayerNum != layerRange.lowerLayerNum) { - Error(impl, "Atomic procedure cannot access global variable"); + Error(ie, "Introduction procedures can modify shared variables only on their introduction layer"); } - this.sharedVarsAccessed = null; } + + procToIntroductionProc[proc] = new IntroductionProc(proc, layerRange, isLeaky); } - if (errorCount > 0) return; - - foreach (var proc in program.Procedures) + if (checkingContext.ErrorCount > 0) return; + + IntroductionProcedureVisitor visitor = new IntroductionProcedureVisitor(this); + foreach (Implementation impl in program.Implementations.Where(impl => procToIntroductionProc.ContainsKey(impl.Proc))) { - if (!QKeyValue.FindBoolAttribute(proc.Attributes, "yields")) continue; + visitor.VisitImplementation(impl); + } + } - int createdAtLayerNum; // must be initialized by the following code, otherwise it is an error - int availableUptoLayerNum = int.MaxValue; - List attrs = FindLayers(proc.Attributes); - if (attrs.Count == 1) + private void TypeCheckYieldingProcedureDecls() + { + YieldingProcVisitor visitor = new YieldingProcVisitor(this); + + foreach (var proc in program.Procedures.Where(proc => proc.IsYield())) + { + int upperLayer; // must be initialized by the following code, otherwise it is an error + List layers = FindLayers(proc.Attributes); + if (layers.Count == 1) { - createdAtLayerNum = attrs[0]; + upperLayer = layers[0]; } - else if (attrs.Count == 2) + else { - createdAtLayerNum = attrs[0]; - availableUptoLayerNum = attrs[1]; + Error(proc, "Expected single layer number for yielding procedure"); + continue; } - else + + string refinesName = QKeyValue.FindStringAttribute(proc.Attributes, CivlAttributes.REFINES); + MoverType? moverType = GetMoverType(proc.Attributes); + if (refinesName != null && moverType.HasValue) { - Error(proc, "Incorrect number of layers"); + Error(proc, "A yielding procedure cannot have both a refines annotation and a mover type"); continue; } - foreach (Ensures e in proc.Ensures) + + if (refinesName != null) // proc is an action procedure { - MoverType moverType = GetMoverType(e); - if (moverType == MoverType.Top) continue; - CodeExpr codeExpr = e.Condition as CodeExpr; - if (codeExpr == null) + AtomicAction refinedAction = procToAtomicAction.Values.Where(a => a.proc.Name == refinesName).FirstOrDefault(); + if (refinedAction == null) { - Error(e, "An atomic action must be a CodeExpr"); + Error(proc, "Could not find refined atomic action"); continue; } - if (procToActionInfo.ContainsKey(proc)) + if (!refinedAction.layerRange.Contains(upperLayer + 1)) { - Error(proc, "A procedure can have at most one atomic action"); - continue; + // Strictly speaking, there could be a layer gap if some layer is not used + // for refinement. However, at this point we do not know the refinement layers, + // so we use this conservative check which seems reasonable in practice. + checkingContext.Error(proc, "Refined atomic action must be available at layer {0}", upperLayer + 1); } - if (availableUptoLayerNum <= createdAtLayerNum) + + CheckSignatures(proc, refinedAction.proc); + + procToYieldingProc[proc] = new ActionProc(proc, refinedAction, upperLayer); + } + else if (moverType.HasValue) // proc is a mover procedure + { + procToYieldingProc[proc] = new MoverProc(proc, moverType.Value, upperLayer); + } + else // proc is a skip procedure + { + procToYieldingProc[proc] = new SkipProc(proc, upperLayer); + } + + visitor.VisitProcedure(proc); + } + } + + private void TypeCheckYieldingProcedureImpls() + { + YieldingProcVisitor visitor = new YieldingProcVisitor(this); + + foreach (var impl in program.Implementations.Where(impl => procToYieldingProc.ContainsKey(impl.Proc))) + { + visitor.VisitImplementation(impl); + + // TODO: Check the modifies clause of mover procedures. + // Calls to introduction procedures! + + YieldingProc yieldingProc = procToYieldingProc[impl.Proc]; + + if (yieldingProc is MoverProc) + { + var declaredModifiedVars = ((MoverProc)yieldingProc).modifiedGlobalVars; + HashSet mods = null; + foreach (var callCmd in impl.Blocks.SelectMany(b => b.Cmds).OfType()) { - Error(proc, "Creation layer number must be less than the available upto layer number"); - continue; + if (procToYieldingProc.ContainsKey(callCmd.Proc)) + { + var calledProc = procToYieldingProc[callCmd.Proc]; + if (calledProc is ActionProc) + { + mods = ((ActionProc)calledProc).refinedAction.layerToActionCopy[yieldingProc.upperLayer].modifiedGlobalVars; + } + else if (calledProc is MoverProc) + { + mods = ((MoverProc)calledProc).modifiedGlobalVars; + } + else + { + Debug.Assert(calledProc is SkipProc); + mods = new HashSet(); + } + } + else if (procToIntroductionProc.ContainsKey(callCmd.Proc)) + { + mods = new HashSet(callCmd.Proc.Modifies.Select(ie => ie.Decl)); + } + else + { + Debug.Assert(false); + } + + foreach (var mod in mods) + { + if (!declaredModifiedVars.Contains(mod)) + { + checkingContext.Error(callCmd, "Modified variable {0} does not appear in modifies clause of mover procedure.", mod.Name); + } + } } + } + } + } + + // Check that an action procedure has the same interface as the refined atomic action. + // CheckSignatures and MatchFormals are adapted from type checking implementations in Absy.cs, + // i.e., that implementations have the same interface as the corresponding procedure. + private void CheckSignatures(Procedure proc, Procedure action) + { + if (proc.TypeParameters.Count != action.TypeParameters.Count) + { + checkingContext.Error(proc, "mismatched number of type parameters in refinement procedure: {0}", proc.Name); + } + else + { + // if the numbers of type parameters are different, it is + // difficult to compare the argument types + MatchFormals(proc, action, proc.InParams, action.InParams, "in"); + MatchFormals(proc, action, proc.OutParams, action.OutParams, "out"); + } + } - sharedVarsAccessed = new HashSet(); - enclosingProc = proc; - enclosingImpl = null; - base.VisitEnsures(e); - LayerRange upperBound = FindLayerRange(); - LayerRange lowerBound = new LayerRange(createdAtLayerNum, availableUptoLayerNum); - if (lowerBound.Subset(upperBound)) + void MatchFormals(Procedure proc, Procedure action, List procFormals, List actionFormals, string inout) + { + if (procFormals.Count != actionFormals.Count) + { + checkingContext.Error(proc, "mismatched number of {0}-parameters in refinement procedure: {1}", inout, proc.Name); + } + else + { + // unify the type parameters so that types can be compared + IDictionary subst1 = new Dictionary(); + IDictionary subst2 = new Dictionary(); + + for (int i = 0; i < proc.TypeParameters.Count; ++i) + { + TypeVariable newVar = new TypeVariable(Token.NoToken, action.TypeParameters[i].Name); + subst1.Add(action.TypeParameters[i], newVar); + subst2.Add(proc.TypeParameters[i], newVar); + } + + for (int i = 0; i < procFormals.Count; i++) + { + // For error messages below + string procName = procFormals[i].Name; + string actionName = actionFormals[i].Name; + string msg; + if (procName == actionName) { - procToActionInfo[proc] = new AtomicActionInfo(proc, e, moverType, createdAtLayerNum, availableUptoLayerNum); + msg = procName; } else { - Error(e, "A variable being accessed in this action is unavailable"); + msg = $"{procName} (named {actionName} in atomic action)"; } - sharedVarsAccessed = null; - } - if (errorCount > 0) continue; - if (!procToActionInfo.ContainsKey(proc)) - { - if (availableUptoLayerNum < createdAtLayerNum) + + // the names of the formals are allowed to change from the proc to the impl + // but types must be identical + Type t = procFormals[i].TypedIdent.Type.Substitute(subst2); + Type u = actionFormals[i].TypedIdent.Type.Substitute(subst1); + if (!t.Equals(u)) { - Error(proc, "Creation layer number must be no more than the available upto layer number"); - continue; + + checkingContext.Error(proc, "mismatched type of {0}-parameter in refinement procedure {1}: {2}", inout, proc.Name, msg); } - else + + if (QKeyValue.FindStringAttribute(procFormals[i].Attributes, CivlAttributes.LINEAR) != QKeyValue.FindStringAttribute(actionFormals[i].Attributes, CivlAttributes.LINEAR) || + QKeyValue.FindStringAttribute(procFormals[i].Attributes, CivlAttributes.LINEAR_IN) != QKeyValue.FindStringAttribute(actionFormals[i].Attributes, CivlAttributes.LINEAR_IN) || + QKeyValue.FindStringAttribute(procFormals[i].Attributes, CivlAttributes.LINEAR_OUT) != QKeyValue.FindStringAttribute(actionFormals[i].Attributes, CivlAttributes.LINEAR_OUT)) { - procToActionInfo[proc] = new ActionInfo(proc, createdAtLayerNum, availableUptoLayerNum); + checkingContext.Error(proc, "mismatched linearity type of {0}-parameter in refinement procedure {1}: {2}", inout, proc.Name, msg); } } } - if (errorCount > 0) return; - - foreach (var impl in program.Implementations) - { - if (!procToActionInfo.ContainsKey(impl.Proc)) continue; - ActionInfo actionInfo = procToActionInfo[impl.Proc]; - procToActionInfo[impl.Proc].hasImplementation = true; - if (actionInfo.isExtern) - { - Error(impl.Proc, "Extern procedure cannot have an implementation"); - } - } - if (errorCount > 0) return; + } - foreach (Procedure proc in procToActionInfo.Keys) + private void TypeCheckLocalVariables() + { + // Local variable with no declared introduction layer implicitely have layer int.MinValue. + // However, to save space and avoid hash collisions, we do not explicitely store variables with layer int.MinValue. + + // First we collect in and out parameter layers from procedures + foreach (Procedure proc in procToYieldingProc.Keys) { - for (int i = 0; i < proc.InParams.Count; i++) - { - Variable v = proc.InParams[i]; - var layer = FindLocalVariableLayer(proc, v, procToActionInfo[proc].createdAtLayerNum); - if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(layer); - } - for (int i = 0; i < proc.OutParams.Count; i++) + foreach (var param in Enumerable.Union(proc.InParams, proc.OutParams)) { - Variable v = proc.OutParams[i]; - var layer = FindLocalVariableLayer(proc, v, procToActionInfo[proc].createdAtLayerNum); - if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(layer); + var layer = FindLocalVariableLayer(proc, param, procToYieldingProc[proc].upperLayer); + if (layer != int.MinValue) + localVarToIntroLayer[param] = layer; } } - foreach (Implementation node in program.Implementations) + foreach (Implementation impl in program.Implementations.Where(i => procToYieldingProc.ContainsKey(i.Proc))) { - if (!procToActionInfo.ContainsKey(node.Proc)) continue; - foreach (Variable v in node.LocVars) + // then we collect the layers of local variables in implementations + foreach (Variable v in impl.LocVars) { - var layer = FindLocalVariableLayer(node, v, procToActionInfo[node.Proc].createdAtLayerNum); - if (layer == int.MinValue) continue; - localVarToLocalVariableInfo[v] = new LocalVariableInfo(layer); + var layer = FindLocalVariableLayer(impl, v, procToYieldingProc[impl.Proc].upperLayer); + if (layer != int.MinValue) + localVarToIntroLayer[v] = layer; } - for (int i = 0; i < node.Proc.InParams.Count; i++) + // and finally just copy the layer information from procedure parameters to their corresponding implementation parameter + // (i.e., layer declarations are only taken from procedures, not implementations) + for (int i = 0; i < impl.Proc.InParams.Count; i++) { - Variable v = node.Proc.InParams[i]; - if (!localVarToLocalVariableInfo.ContainsKey(v)) continue; - var layer = localVarToLocalVariableInfo[v].layer; - localVarToLocalVariableInfo[node.InParams[i]] = new LocalVariableInfo(layer); + Variable v = impl.Proc.InParams[i]; + if (localVarToIntroLayer.ContainsKey(v)) + localVarToIntroLayer[impl.InParams[i]] = localVarToIntroLayer[v]; } - for (int i = 0; i < node.Proc.OutParams.Count; i++) + for (int i = 0; i < impl.Proc.OutParams.Count; i++) { - Variable v = node.Proc.OutParams[i]; - if (!localVarToLocalVariableInfo.ContainsKey(v)) continue; - var layer = localVarToLocalVariableInfo[v].layer; - localVarToLocalVariableInfo[node.OutParams[i]] = new LocalVariableInfo(layer); + Variable v = impl.Proc.OutParams[i]; + if (localVarToIntroLayer.ContainsKey(v)) + localVarToIntroLayer[impl.OutParams[i]] = localVarToIntroLayer[v]; } } - if (errorCount > 0) return; - - this.VisitProgram(program); - if (errorCount > 0) return; - YieldTypeChecker.PerformYieldSafeCheck(this); - new LayerEraser().VisitProgram(program); } - public IEnumerable SharedVariables + public int LocalVariableIntroLayer(Variable l) { - get { return this.globalVarToSharedVarInfo.Keys; } + if (localVarToIntroLayer.ContainsKey(l)) + return localVarToIntroLayer[l]; + return int.MinValue; + } + + public int AllInParamsIntroducedLayer(Procedure proc) + { + return proc.InParams.Select(inParam => LocalVariableIntroLayer(inParam)).DefaultIfEmpty(int.MinValue).Max(); } private int FindLocalVariableLayer(Declaration decl, Variable v, int enclosingProcLayerNum) @@ -755,405 +1007,810 @@ return layers[0]; } - public override Implementation VisitImplementation(Implementation node) + /// + /// Fixpoint computation to determine for each atomic action the least layer at which it becomes async free. + /// + private void ComputeAsyncFreeLayers() { - if (!procToActionInfo.ContainsKey(node.Proc)) + foreach (var a in procToAtomicAction.Values) { - return node; + a.asyncFreeLayer = a.layerRange.lowerLayerNum; + } + + bool proceed = true; + while (proceed) + { + proceed = false; + foreach (var a in procToAtomicAction.Values) + { + foreach (var call in a.impl.Blocks.SelectMany(b => b.cmds.OfType())) + { + var target = procToYieldingProc[call.Proc]; + int x = target.upperLayer + 1; + if (target is ActionProc actionProc) + { + x = Math.Max(x, actionProc.refinedAction.asyncFreeLayer); + } + if (x > a.asyncFreeLayer) + { + a.asyncFreeLayer = x; + proceed = true; + } + } + } } - this.enclosingImpl = node; - this.enclosingProc = null; - return base.VisitImplementation(node); } - public override Procedure VisitProcedure(Procedure node) + // TODO: Is it necessary to construct a graph for every layer independently? + /// + /// Check (for each layer individually), if atomic actions have a cyclic dependency through pending asyncs. + /// + private void CheckAtomicActionAcyclicity() { - if (!procToActionInfo.ContainsKey(node)) + foreach (var layer in allAtomicActionLayers) { - return node; + Graph graph = new Graph(); + + foreach (var action in procToAtomicAction.Values.Where(a => a.layerRange.Contains(layer))) + { + foreach (var callCmd in action.impl.Blocks.SelectMany(b => b.Cmds).OfType()) + { + Debug.Assert(callCmd.IsAsync); + + if (procToYieldingProc[callCmd.Proc] is ActionProc calleeProc && layer > calleeProc.upperLayer) + { + graph.AddEdge(action, calleeProc.refinedAction); + } + } + } + + if (!Graph.Acyclic(graph, null)) + checkingContext.Error(Token.NoToken, "Atomic actions have cyclic dependency at layer " + layer); } - this.enclosingProc = node; - this.enclosingImpl = null; - return base.VisitProcedure(node); } - public override Cmd VisitCallCmd(CallCmd node) + private void AddPendingAsyncMachinery() { - int enclosingProcLayerNum = procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; - if (procToActionInfo.ContainsKey(node.Proc)) + CtorType pendingAsyncType = null; + + // We do not want to disturb non-CIVL programs + if (procToAtomicAction.Count != 0) { - ActionInfo actionInfo = procToActionInfo[node.Proc]; - if (node.IsAsync && actionInfo is AtomicActionInfo) + // datatype + pendingAsyncType = new CtorType(Token.NoToken, new TypeCtorDecl(Token.NoToken, "PendingAsync", 0, new QKeyValue(Token.NoToken, "datatype", new List(), null)), new List()); + program.AddTopLevelDeclaration(pendingAsyncType.Decl); + + // global multiset variable + MapType pendingAsyncMultisetType = new MapType(Token.NoToken, new List(), new List{ pendingAsyncType }, Type.Int); + this.pendingAsyncMultiset = new GlobalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "pendingAsyncMultiset", pendingAsyncMultisetType)); + program.AddTopLevelDeclaration(pendingAsyncMultiset); + } + + foreach (var actionProc in procToYieldingProc.Values.OfType()) + { + Debug.Assert(pendingAsyncType != null); + + // constructor function + Function f = new Function( + Token.NoToken, + "PendingAsync_" + actionProc.proc.Name, + new List(), + actionProc.proc.InParams.Select(v => new Formal(Token.NoToken, new TypedIdent(Token.NoToken, v.Name, v.TypedIdent.Type), true)).ToList(), + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "x", pendingAsyncType), false), + null, + new QKeyValue(Token.NoToken, "constructor", new List(), null) + ); + DatatypeConstructor c = new DatatypeConstructor(f); + program.AddTopLevelDeclaration(c); + + // pending async adder procedure & implementation + string name = "AddPendingAsync_" + actionProc.proc.Name; + Procedure p = new Procedure( + Token.NoToken, + name, + new List(), + actionProc.proc.InParams.Select(v => (Variable)v.Clone()).ToList(), + new List(), + new List(), + new List { Expr.Ident(pendingAsyncMultiset) }, + new List() + ); + p.InParams.ForEach(inParam => inParam.Attributes = null); + CivlUtil.AddInlineAttribute(p); + p.Typecheck(new TypecheckingContext(null)); + actionProc.addPendingAsyncProc = p; + program.AddTopLevelDeclaration(p); + + var inParams = actionProc.proc.InParams.Select(v => (Variable)v.Clone()).ToList(); + inParams.ForEach(inParam => inParam.Attributes = null); + Expr idx = new NAryExpr( + Token.NoToken, + new FunctionCall(c), + inParams.Select(v => Expr.Ident(v)).ToList()); + + MapAssignLhs lhs = new MapAssignLhs(Token.NoToken, new SimpleAssignLhs(Token.NoToken, Expr.Ident(pendingAsyncMultiset)), new List { idx }); + Expr sel = Expr.Select(Expr.Ident(pendingAsyncMultiset), new List{ idx }); + Expr plusOne = Expr.Add(sel, new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(1))); + AssignCmd cmd = new AssignCmd(Token.NoToken, new List{ lhs }, new List { plusOne }); + + Implementation i = new Implementation( + Token.NoToken, + name, + new List(), + inParams, + new List(), + new List(), + new List { new Block(Token.NoToken, "L", new List { cmd }, new ReturnCmd(Token.NoToken)) }); + i.Proc = p; + i.OriginalBlocks = i.Blocks; + i.OriginalLocVars = i.LocVars; + CivlUtil.AddInlineAttribute(i); + i.Typecheck(new TypecheckingContext(null)); + program.AddTopLevelDeclaration(i); + } + } + + private void GenerateAtomicActionCopies() + { + foreach (var layer in allAtomicActionLayers) + { + // Generate layer specific versions of atomic actions (i.e., resolve pending async calls) + AtomicActionCopier copier = new AtomicActionCopier(layer, this); + copier.GenerateCopies(); + + // AtomicActionCopy constructur removes gate from atomic action implementation + foreach (var action in copier.actions) { - Error(node, "Target of async call cannot be an atomic action"); + action.layerToActionCopy[layer] = new AtomicActionCopy(copier.procMap[action.proc], copier.implMap[action.impl]); } - int calleeLayerNum = procToActionInfo[node.Proc].createdAtLayerNum; - if (enclosingProcLayerNum < calleeLayerNum || - (enclosingProcLayerNum == calleeLayerNum && actionInfo is AtomicActionInfo)) + + program.AddTopLevelDeclarations(copier.procMap.Values); + program.AddTopLevelDeclarations(copier.implMap.Values); + + // Fully inline atomic action implementations + foreach (var impl in copier.implMap.Values) { - Error(node, "The layer of the caller must be greater than the layer of the callee"); + impl.OriginalBlocks = impl.Blocks; + impl.OriginalLocVars = impl.LocVars; } - else if (enclosingProcLayerNum == calleeLayerNum && enclosingImpl.OutParams.Count > 0) + foreach (var impl in copier.implMap.Values) { - HashSet outParams = new HashSet(enclosingImpl.OutParams); - foreach (var x in node.Outs) - { - if (x.Decl is GlobalVariable) - { - Error(node, "A global variable cannot be used as output argument for this call"); - } - else if (outParams.Contains(x.Decl)) - { - Error(node, "An output variable of the enclosing implementation cannot be used as output argument for this call"); - } - } + Inliner.ProcessImplementation(program, impl); } - if (actionInfo.availableUptoLayerNum < enclosingProcLayerNum) + foreach (var impl in copier.implMap.Values) { - Error(node, "The callee is not available in the caller procedure"); + impl.OriginalBlocks = null; + impl.OriginalLocVars = null; } - for (int i = 0; i < node.Ins.Count; i++) + + // Generate first/second versions of atomic actions (used in transition relation computation) + // and initialize used/modified variable infos. + foreach (var action in copier.actions) { - Visit(node.Ins[i]); - if (introducedLocalVarsUpperBound != int.MinValue) - { - var formal = node.Proc.InParams[i]; - if (!localVarToLocalVariableInfo.ContainsKey(formal) || - introducedLocalVarsUpperBound > localVarToLocalVariableInfo[formal].layer) - { - Error(node, "An introduced local variable is accessed but not available"); - } - introducedLocalVarsUpperBound = int.MinValue; - } + action.layerToActionCopy[layer].Setup(); } - for (int i = 0; i < node.Outs.Count; i++) + } + } + + public class AtomicActionCopier : Duplicator + { + private int layer; + private CivlTypeChecker ctc; + + public List actions; + public Dictionary procMap; + public Dictionary implMap; + + public AtomicActionCopier(int layer, CivlTypeChecker ctc) + { + this.layer = layer; + this.ctc = ctc; + this.actions = ctc.procToAtomicAction.Values.Where(a => a.layerRange.Contains(layer)).ToList(); + this.procMap = new Dictionary(); + this.implMap = new Dictionary(); + } + + public void GenerateCopies() + { + foreach (var action in actions) { - var formal = node.Proc.OutParams[i]; - if (!localVarToLocalVariableInfo.ContainsKey(formal)) continue; - var actual = node.Outs[i].Decl; - if (localVarToLocalVariableInfo.ContainsKey(actual) && - localVarToLocalVariableInfo[formal].layer <= localVarToLocalVariableInfo[actual].layer) - continue; - Error(node, "Formal parameter of call must be introduced no later than the actual parameter"); + VisitProcedure(action.proc); + } + foreach (var action in actions) + { + VisitImplementation(action.impl); } + } + + public override Procedure VisitProcedure(Procedure node) + { + if (procMap.ContainsKey(node)) + return procMap[node]; + + Procedure proc = base.VisitProcedure(node); + proc.Name = $"{node.Name}_{layer}"; + procMap[node] = proc; + CivlUtil.AddInlineAttribute(proc); + return proc; + } + + public override Implementation VisitImplementation(Implementation node) + { + if (implMap.ContainsKey(node)) + return implMap[node]; + + Procedure proc = procMap[node.Proc]; + Implementation impl = base.VisitImplementation(node); + impl.Proc = proc; + impl.Name = proc.Name; + implMap[node] = impl; + CivlUtil.AddInlineAttribute(impl); + return impl; + } + + public override Cmd VisitCallCmd(CallCmd node) + { + CallCmd call = (CallCmd)base.VisitCallCmd(node); + call.IsAsync = false; + Debug.Assert(ctc.procToYieldingProc[node.Proc] is ActionProc); + ActionProc target = (ActionProc)ctc.procToYieldingProc[node.Proc]; + if (target.upperLayer >= layer) + call.Proc = target.addPendingAsyncProc; + else + call.Proc = procMap[target.refinedAction.proc]; + call.callee = call.Proc.Name; + return call; + } + } + + public void Error(Absy node, string message) + { + checkingContext.Error(node, message); + } + + private class AtomicActionVisitor : ReadOnlyVisitor + { + private CivlTypeChecker ctc; + private AtomicAction atomicAction; + + public AtomicActionVisitor(CivlTypeChecker ctc) + { + this.ctc = ctc; + } + + internal void VisitAction(AtomicAction atomicAction) + { + this.atomicAction = atomicAction; + VisitImplementation(atomicAction.impl); + } + + public override Procedure VisitProcedure(Procedure node) + { + // This visitor only has to check the body of atomic action specifications return node; } - else if (procToAtomicProcedureInfo.ContainsKey(node.Proc)) + + public override Implementation VisitImplementation(Implementation node) + { + return base.VisitImplementation(node); + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) + { + if (node.Decl is GlobalVariable) + { + var sharedVarLayerRange = ctc.GlobalVariableLayerRange(node.Decl); + if (!atomicAction.layerRange.Subset(sharedVarLayerRange) || sharedVarLayerRange.lowerLayerNum == atomicAction.layerRange.lowerLayerNum) + // a shared variable introduced at layer n is visible to an atomic action only at layer n+1 or higher + // thus, a shared variable with layer range [n,n] is not accessible by an atomic action + { + ctc.checkingContext.Error(node, "Shared variable {0} is not available in atomic action specification", node.Decl.Name); + } + } + return base.VisitIdentifierExpr(node); + } + + public override Cmd VisitCallCmd(CallCmd node) { - var atomicProcedureInfo = procToAtomicProcedureInfo[node.Proc]; - if (atomicProcedureInfo.isPure) + if (node.IsAsync) { - if (node.Outs.Count > 0) + if (atomicAction.moverType != MoverType.Atomic && atomicAction.moverType != MoverType.Left) { - int inferredLayer = int.MinValue; - foreach (var ie in node.Outs) - { - if (!localVarToLocalVariableInfo.ContainsKey(ie.Decl)) continue; - if (inferredLayer < localVarToLocalVariableInfo[ie.Decl].layer) - { - inferredLayer = localVarToLocalVariableInfo[ie.Decl].layer; - } - } - pureCallLayer[node] = inferredLayer; - if (inferredLayer != int.MinValue) + ctc.Error(node, "Atomic action with pending async must have mover type atomic or left"); + } + if (!ctc.procToYieldingProc.ContainsKey(node.Proc) || !(ctc.procToYieldingProc[node.Proc] is ActionProc)) + { + ctc.Error(node, "Target of a pending async must be an action procedure"); + } + else + { + ActionProc target = (ActionProc)ctc.procToYieldingProc[node.Proc]; + if (!target.IsLeftMover) { - foreach (var ie in node.Outs) - { - if (!localVarToLocalVariableInfo.ContainsKey(ie.Decl)) - { - Error(node, "Output variable must be introduced"); - } - else if (inferredLayer != localVarToLocalVariableInfo[ie.Decl].layer) - { - Error(node, "All output variables must be introduced at the same layer"); - } - } + ctc.Error(node, "Target of async call must be a left mover"); } - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - foreach (var e in node.Ins) + if (target.refinedAction.layerRange.upperLayerNum < atomicAction.layerRange.upperLayerNum) { - Visit(e); - if (inferredLayer < introducedLocalVarsUpperBound) - { - Error(node, "An introduced local variable is not accessible"); - } - introducedLocalVarsUpperBound = int.MinValue; + ctc.Error(node, "Callee disappears before caller"); } - } - else - { - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - int inferredLayer = int.MinValue; - foreach (var e in node.Ins) + if (ctc.AllInParamsIntroducedLayer(target.proc) > atomicAction.layerRange.lowerLayerNum) { - Visit(e); - if (inferredLayer < introducedLocalVarsUpperBound) - { - inferredLayer = introducedLocalVarsUpperBound; - } - introducedLocalVarsUpperBound = int.MinValue; + ctc.Error(node, "Target of a pending async must have all input variables introduced"); } - pureCallLayer[node] = inferredLayer; } } else { - if (enclosingProcLayerNum != atomicProcedureInfo.layerRange.upperLayerNum) - { - Error(node, "Creation layer of caller must be the upper bound of the layer range of callee"); - } - foreach (var ie in node.Proc.Modifies) - { - if (enclosingProcLayerNum != globalVarToSharedVarInfo[ie.Decl].introLayerNum) - { - Error(node, "Creation layer of caller must be identical to the introduction layer of modified variable"); - } - } - foreach (var ie in node.Outs) - { - if (localVarToLocalVariableInfo.ContainsKey(ie.Decl) && - enclosingProcLayerNum == localVarToLocalVariableInfo[ie.Decl].layer) - continue; - Error(node, "Output variable must be introduced at the creation layer of caller"); - } + ctc.Error(node, "Call command not allowed inside an atomic action"); } - return node; - } - else - { - Error(node, "A yielding procedure can call only atomic or yielding procedures"); - return node; + return base.VisitCallCmd(node); } } - public override Cmd VisitParCallCmd(ParCallCmd node) + private class IntroductionProcedureVisitor : ReadOnlyVisitor { - int enclosingProcLayerNum = procToActionInfo[enclosingImpl.Proc].createdAtLayerNum; - bool isLeftMover = true; - bool isRightMover = true; - int maxCalleeLayerNum = 0; - int atomicActionCalleeLayerNum = 0; - int numAtomicActions = 0; - foreach (CallCmd iter in node.CallCmds) + private CivlTypeChecker ctc; + private IntroductionProc introductionProc; + + public IntroductionProcedureVisitor(CivlTypeChecker civlTypeChecker) { - ActionInfo actionInfo = procToActionInfo[iter.Proc]; - isLeftMover = isLeftMover && actionInfo.IsLeftMover; - isRightMover = isRightMover && actionInfo.IsRightMover; - if (actionInfo.createdAtLayerNum > maxCalleeLayerNum) + this.ctc = civlTypeChecker; + } + + public override Implementation VisitImplementation(Implementation node) + { + introductionProc = ctc.procToIntroductionProc[node.Proc]; + return base.VisitImplementation(node); + } + + public override Cmd VisitCallCmd(CallCmd callCmd) + { + if (!ctc.procToIntroductionProc.ContainsKey(callCmd.Proc)) { - maxCalleeLayerNum = actionInfo.createdAtLayerNum; + ctc.Error(callCmd, "Introduction procedure can only call an introduction procedure"); + return base.VisitCallCmd(callCmd); } - if (actionInfo is AtomicActionInfo) + IntroductionProc calleeProc = ctc.procToIntroductionProc[callCmd.Proc]; + if (!introductionProc.layerRange.Subset(calleeProc.layerRange)) { - numAtomicActions++; - if (atomicActionCalleeLayerNum == 0) - { - atomicActionCalleeLayerNum = actionInfo.createdAtLayerNum; - } - else if (atomicActionCalleeLayerNum != actionInfo.createdAtLayerNum) + ctc.Error(callCmd, "Caller layers must be subset of callee layers"); + } + return base.VisitCallCmd(callCmd); + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) + { + if (node.Decl is GlobalVariable) + { + LayerRange globalVarLayerRange = ctc.GlobalVariableLayerRange(node.Decl); + if (!introductionProc.layerRange.Subset(globalVarLayerRange)) { - Error(node, "All atomic actions must be introduced at the same layer"); + ctc.Error(node, "Shared variable is not accessible in introduction procedure"); } } + return node; } - if (numAtomicActions > 1 && !isLeftMover && !isRightMover) + } + + private class YieldingProcVisitor : ReadOnlyVisitor + { + CivlTypeChecker ctc; + YieldingProc yieldingProc; + List sharedVariableAccesses; + List localVariableAccesses; + + Procedure enclosingProc; + Implementation enclosingImpl; + + public YieldingProcVisitor(CivlTypeChecker ctc) { - Error(node, "The atomic actions in the parallel call must be all right movers or all left movers"); + this.ctc = ctc; + + sharedVariableAccesses = null; + localVariableAccesses = null; + + enclosingImpl = null; } - if (0 < atomicActionCalleeLayerNum && atomicActionCalleeLayerNum < maxCalleeLayerNum) + + public override Implementation VisitImplementation(Implementation node) { - Error(node, "Atomic actions must be introduced at the highest layer"); + Debug.Assert(yieldingProc == null); + enclosingImpl = node; + yieldingProc = ctc.procToYieldingProc[node.Proc]; + var ret = base.VisitImplementation(node); + enclosingImpl = null; + yieldingProc = null; + return ret; } - return base.VisitParCallCmd(node); - } - public override Expr VisitIdentifierExpr(IdentifierExpr node) - { - if (node.Decl is GlobalVariable) + public override Procedure VisitProcedure(Procedure node) { - if (sharedVarsAccessed == null) + if (yieldingProc != null) { - Error(node, "Shared variable can be accessed only in atomic actions or specifications"); + // We don't want to descend from an implementation into the corresponding procedure, + // otherwise the procedure would be visited twice, causing duplicate error messages. + return node; } - else if (this.globalVarToSharedVarInfo.ContainsKey(node.Decl)) + enclosingProc = node; + yieldingProc = ctc.procToYieldingProc[node]; + var ret = base.VisitProcedure(node); + enclosingProc = null; + yieldingProc = null; + return ret; + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) + { + if (node.Decl is GlobalVariable) { - sharedVarsAccessed.Add(node.Decl); + if (sharedVariableAccesses != null) + { + sharedVariableAccesses.Add(node); + } + else if (enclosingProc != null) + { + // Modifies clauses of mover procedures need access to global variables. + } + else + { + ctc.Error(node, "Shared variable can be accessed only in introduction procedures, atomic actions, and specifications"); + } } else { - Error(node, "Accessed shared variable must have layer annotation"); + if (localVariableAccesses != null) + { + localVariableAccesses.Add(node); + } } + return base.VisitIdentifierExpr(node); } - else if ((node.Decl is Formal || node.Decl is Variable) && localVarToLocalVariableInfo.ContainsKey(node.Decl)) - { - var localVariableInfo = localVarToLocalVariableInfo[node.Decl]; - if (introducedLocalVarsUpperBound < localVariableInfo.layer) - { - introducedLocalVarsUpperBound = localVariableInfo.layer; - } - } - return base.VisitIdentifierExpr(node); - } - public override Ensures VisitEnsures(Ensures ensures) - { - ActionInfo actionInfo = procToActionInfo[enclosingProc]; - AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; - if (atomicActionInfo != null && atomicActionInfo.ensures == ensures) + // All preconditions, postconditions, and assertions are treated similarly: + // * VisitSpecPre activates collection of accessed global and local variables + // * Call to base visitor actually does the collection + // * VisitSpecPost checks the consistency of layer annotations + + public override Ensures VisitEnsures(Ensures ensures) { - // This case has already been checked + VisitSpecPre(); + base.VisitEnsures(ensures); + VisitSpecPost(ensures); + return ensures; } - else + + public override Requires VisitRequires(Requires requires) { - sharedVarsAccessed = new HashSet(); - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - base.VisitEnsures(ensures); - CheckAndAddLayers(ensures, ensures.Attributes, actionInfo.createdAtLayerNum); - if (introducedLocalVarsUpperBound > Least(FindLayers(ensures.Attributes))) - { - Error(ensures, "An introduced local variable is accessed but not available"); - } - introducedLocalVarsUpperBound = int.MinValue; - sharedVarsAccessed = null; + VisitSpecPre(); + base.VisitRequires(requires); + VisitSpecPost(requires); + return requires; } - return ensures; - } - public override Requires VisitRequires(Requires requires) - { - sharedVarsAccessed = new HashSet(); - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - base.VisitRequires(requires); - CheckAndAddLayers(requires, requires.Attributes, procToActionInfo[enclosingProc].createdAtLayerNum); - if (introducedLocalVarsUpperBound > Least(FindLayers(requires.Attributes))) + public override Cmd VisitAssertCmd(AssertCmd assert) { - Error(requires, "An introduced local variable is accessed but not available"); + VisitSpecPre(); + base.VisitAssertCmd(assert); + VisitSpecPost(assert); + return assert; } - introducedLocalVarsUpperBound = int.MinValue; - sharedVarsAccessed = null; - return requires; - } - public override Cmd VisitAssertCmd(AssertCmd node) - { - if (enclosingImpl == null) + public void VisitSpecPre() { - // in this case, we are visiting an assert inside a CodeExpr - return base.VisitAssertCmd(node); + sharedVariableAccesses = new List(); + localVariableAccesses = new List(); } - sharedVarsAccessed = new HashSet(); - Debug.Assert(introducedLocalVarsUpperBound == int.MinValue); - base.VisitAssertCmd(node); - CheckAndAddLayers(node, node.Attributes, procToActionInfo[enclosingImpl.Proc].createdAtLayerNum); - if (introducedLocalVarsUpperBound > Least(FindLayers(node.Attributes))) + + public void VisitSpecPost(T node) + where T : Absy, ICarriesAttributes { - Error(node, "An introduced local variable is accessed but not available"); - } - introducedLocalVarsUpperBound = int.MinValue; - sharedVarsAccessed = null; - return node; - } + var specLayers = ctc.FindLayers(node.Attributes).Distinct().OrderBy(l => l).ToList(); + if (specLayers.Count == 0) + { + ctc.Error(node, "Specification layer(s) not present"); + return; + } - private List RemoveDuplicatesAndSort(List attrs) - { - HashSet layerSet = new HashSet(attrs); - List layers = new List(layerSet); - layers.Sort(); - return layers; - } + ctc.absyToLayerNums[node] = new HashSet(specLayers); - private void CheckAndAddLayers(Absy node, QKeyValue attributes, int enclosingProcLayerNum) - { - List attrs = RemoveDuplicatesAndSort(FindLayers(attributes)); - if (attrs.Count == 0) - { - Error(node, "layer not present"); - return; + foreach (var layer in specLayers) + { + if (layer > yieldingProc.upperLayer) + { + ctc.checkingContext.Error(node, "Specification layer {0} is greater than enclosing procedure layer {1}", layer, yieldingProc.upperLayer); + } + foreach (var ie in sharedVariableAccesses) + { + if (!ctc.GlobalVariableLayerRange(ie.Decl).Contains(layer)) + { + ctc.checkingContext.Error(ie, "Global variable {0} is not available at layer {1}", ie.Name, layer); + } + } + foreach (var ie in localVariableAccesses) + { + if (layer < ctc.LocalVariableIntroLayer(ie.Decl)) + { + ctc.checkingContext.Error(ie, "Local variable {0} is not available at layer {1}", ie.Name, layer); + } + } + } + + sharedVariableAccesses = null; + localVariableAccesses = null; } - LayerRange upperBound = FindLayerRange(); - absyToLayerNums[node] = new HashSet(); - foreach (int layerNum in attrs) + + public override Cmd VisitCallCmd(CallCmd call) { - if (layerNum > enclosingProcLayerNum) + YieldingProc callerProc = yieldingProc; + + if (ctc.procToYieldingProc.ContainsKey(call.Proc)) { - Error(node, "The layer cannot be greater than the layer of enclosing procedure"); + VisitYieldingProcCallCmd(call, callerProc, ctc.procToYieldingProc[call.Proc]); } - else if (upperBound.Contains(layerNum)) + else if (ctc.procToIntroductionProc.ContainsKey(call.Proc)) { - absyToLayerNums[node].Add(layerNum); + VisitIntroductionProcCallCmd(call, callerProc, ctc.procToIntroductionProc[call.Proc]); } else { - Error(node, string.Format("A variable being accessed in this specification is unavailable at layer {0}", layerNum)); + ctc.Error(call, "A yielding procedure can only call yielding or introduction procedures"); } + return call; } - } - public void Error(Absy node, string message) - { - checkingContext.Error(node, message); - errorCount++; - } - - private class PurityChecker : StandardVisitor - { - private CivlTypeChecker civlTypeChecker; - - public PurityChecker(CivlTypeChecker civlTypeChecker) + private void VisitYieldingProcCallCmd(CallCmd call, YieldingProc callerProc, YieldingProc calleeProc) { - this.civlTypeChecker = civlTypeChecker; - } - - public override Cmd VisitCallCmd(CallCmd node) - { - Procedure enclosingProc = civlTypeChecker.enclosingImpl.Proc; - if (!civlTypeChecker.procToAtomicProcedureInfo.ContainsKey(node.Proc)) + // Skip and mover procedures have to be async-free at their upper layer + if (callerProc is SkipProc || callerProc is MoverProc) + { + if (call.IsAsync && calleeProc.upperLayer > callerProc.upperLayer) + { + ctc.Error(call, "Disappearing layer of caller cannot be lower than that of callee"); + } + else + { + if (calleeProc is ActionProc actionProc && actionProc.refinedAction.asyncFreeLayer > callerProc.upperLayer) + { + ctc.Error(call, "Disappearing layer of caller cannot be lower than async-free layer of callee"); + } + } + } + + if (calleeProc is ActionProc) + { + if (callerProc.upperLayer == calleeProc.upperLayer) + { + ctc.Error(call, "The layer of the caller must be greater than the layer of the callee"); + } + else if (callerProc.upperLayer < calleeProc.upperLayer) + { + if (!call.IsAsync) + { + ctc.Error(call, "The layer of the caller must be greater than the layer of the callee"); + } + else if (ctc.AllInParamsIntroducedLayer(calleeProc.proc) > callerProc.upperLayer) + { + ctc.Error(call, "Target of a pending async must have all input variables introduced"); + } + } + if (((ActionProc)calleeProc).refinedAction.layerRange.upperLayerNum < callerProc.upperLayer) + { + ctc.Error(call, "The callee is not available in the caller procedure"); + } + } + else if (calleeProc is SkipProc) + { + if (callerProc is MoverProc && callerProc.upperLayer <= calleeProc.upperLayer) + { + ctc.Error(call, "The layer of the caller must be greater than the layer of the callee"); + } + else if (callerProc.upperLayer < calleeProc.upperLayer) + { + ctc.Error(call, "The layer of the caller must be greater than or equal to the layer of the callee"); + } + else if (callerProc.upperLayer == calleeProc.upperLayer && enclosingImpl.OutParams.Count > 0) + { + // Skip procedures have the effect of havocing their output variables. + // Currently, the snapshotting during refinement checking does not account for that, + // so we forbid propagation to the callers output variables. + HashSet callerOutParams = new HashSet(enclosingImpl.OutParams); + foreach (var x in call.Outs) + { + if (callerOutParams.Contains(x.Decl)) + { + ctc.Error(call, "An output variable of the enclosing implementation cannot be used as output argument for this call"); + } + } + } + } + else if (calleeProc is MoverProc) { - civlTypeChecker.Error(node, "Atomic procedure can only call an atomic procedure"); - return base.VisitCallCmd(node); + if (callerProc.upperLayer != calleeProc.upperLayer) + { + ctc.Error(call, "The layer of the caller must be equal to the layer of the callee"); + } } - var callerInfo = civlTypeChecker.procToAtomicProcedureInfo[enclosingProc]; - var calleeInfo = civlTypeChecker.procToAtomicProcedureInfo[node.Proc]; - if (calleeInfo.isPure) + + if (call.IsAsync && !calleeProc.IsLeftMover) { - // do nothing + ctc.Error(call, "Target of async call must be a left mover"); } - else if (callerInfo.isPure) + + for (int i = 0; i < call.Ins.Count; i++) { - civlTypeChecker.Error(node, "Pure procedure can only call pure procedures"); + // Visitor checks for global variable accesses and collects accessed local variables + localVariableAccesses = new List(); + Visit(call.Ins[i]); + + var formalIntroLayer = ctc.LocalVariableIntroLayer(call.Proc.InParams[i]); + foreach (var ie in localVariableAccesses) + { + var actualIntroLayer = ctc.LocalVariableIntroLayer(ie.Decl); + if (ctc.LocalVariableIntroLayer(ie.Decl) > formalIntroLayer) + { + ctc.checkingContext.Error(ie, "Variable {0} introduced on layer {1} can not be passed to formal parameter introduced on layer {2}", ie.Decl.Name, actualIntroLayer, formalIntroLayer); + } + } + + localVariableAccesses = null; } - else if (!callerInfo.layerRange.Subset(calleeInfo.layerRange)) + + for (int i = 0; i < call.Outs.Count; i++) { - civlTypeChecker.Error(node, "Caller layers must be subset of callee layers"); + Variable formal = call.Proc.OutParams[i]; + IdentifierExpr actualIdentifierExpr = call.Outs[i]; + Variable actual = actualIdentifierExpr.Decl; + int formalIntroLayer = ctc.LocalVariableIntroLayer(formal); + int actualIntroLayer = ctc.LocalVariableIntroLayer(actual); + + // Visitor only called to check for global variable accesses + Visit(actualIdentifierExpr); + + if (actualIntroLayer < formalIntroLayer) + { + ctc.Error(actualIdentifierExpr, "Formal return parameter of call must be introduced no later than the actual parameter"); + } } - return base.VisitCallCmd(node); } - public override Cmd VisitParCallCmd(ParCallCmd node) + private void VisitIntroductionProcCallCmd(CallCmd call, YieldingProc callerProc, IntroductionProc calleeProc) { - civlTypeChecker.Error(node, "Atomic procedures cannot make parallel calls"); - return node; + if (!calleeProc.layerRange.Contains(callerProc.upperLayer)) + { + ctc.checkingContext.Error(call, "Called introduction procedure {0} is not available at layer {1}", calleeProc.proc.Name, callerProc.upperLayer); + return; + } + if (calleeProc.isLeaky) + { + // Call to leaky introduction procedure only exists at the upper layer of caller yielding procedure. + // Thus, all local variables are already introduced and we only have to check output variables. + foreach (var ie in call.Outs) + { + if (ctc.LocalVariableIntroLayer(ie.Decl) != callerProc.upperLayer) + { + ctc.checkingContext.Error(ie, "Output variable {0} must be introduced at layer {1}", ie.Decl.Name, callerProc.upperLayer); + } + } + } + else + { + // Call to non-leaky introduction procedure exists as soon as all local variables used as input are available. + // I.e., we compute the maximum introduction layer of all local variables used as input. + localVariableAccesses = new List(); + foreach (var e in call.Ins) { Visit(e); } + ctc.introductionCallToLayer[call] = localVariableAccesses + .Select(ie => ctc.LocalVariableIntroLayer(ie.Decl)) + .Concat1(calleeProc.layerRange.lowerLayerNum) + .Max(); + localVariableAccesses = null; + } } - public override Expr VisitIdentifierExpr(IdentifierExpr node) + public override Cmd VisitParCallCmd(ParCallCmd parCall) { - Procedure enclosingProc = civlTypeChecker.enclosingImpl.Proc; - if (node.Decl is GlobalVariable) + bool allLeftMover = true; + bool allRightMover = true; + int maxCalleeLayerNum = int.MinValue; + int atomicActionCalleeLayerNum = int.MinValue; + int numAtomicActions = 0; + foreach (CallCmd iter in parCall.CallCmds) { - if (civlTypeChecker.procToAtomicProcedureInfo[enclosingProc].isPure) + YieldingProc callee = ctc.procToYieldingProc[iter.Proc]; + allLeftMover = allLeftMover && callee.IsLeftMover; + allRightMover = allRightMover && callee.IsRightMover; + + if (callee is MoverProc) { - civlTypeChecker.Error(node, "Pure procedure cannot access global variables"); - } - else if (!civlTypeChecker.globalVarToSharedVarInfo.ContainsKey(node.Decl)) + ctc.Error(parCall, "Mover procedure cannot be part of a parallel call"); + } + else if (callee is ActionProc) { - civlTypeChecker.Error(node, "Atomic procedure cannot access a global variable without layer numbers"); - } - else + numAtomicActions++; + if (atomicActionCalleeLayerNum == int.MinValue) + { + atomicActionCalleeLayerNum = callee.upperLayer; + } + else if (atomicActionCalleeLayerNum != callee.upperLayer) + { + ctc.Error(parCall, "All atomic actions must be introduced at the same layer"); + } + } + + if (callee.upperLayer > maxCalleeLayerNum) { - civlTypeChecker.sharedVarsAccessed.Add(node.Decl); + maxCalleeLayerNum = callee.upperLayer; } } - return node; + if (numAtomicActions > 1 && !allLeftMover && !allRightMover) + { + // TODO: Error message is misleading, since there can be a single non-mover. + ctc.Error(parCall, "The atomic actions in the parallel call must be all right movers or all left movers"); + } + if (atomicActionCalleeLayerNum != int.MinValue && atomicActionCalleeLayerNum < maxCalleeLayerNum) + { + ctc.Error(parCall, "Atomic actions must be introduced at the highest layer"); + } + return base.VisitParCallCmd(parCall); + } + + public override YieldCmd VisitYieldCmd(YieldCmd node) + { + if (yieldingProc is MoverProc) + { + ctc.Error(node, "A mover procedure cannot contain explicit yield statements"); + } + return base.VisitYieldCmd(node); + } + } + + private class AttributeEraser : ReadOnlyVisitor + { + public override Procedure VisitProcedure(Procedure node) + { + CivlAttributes.RemoveYieldsAttribute(node); + CivlAttributes.RemoveMoverAttribute(node); + CivlAttributes.RemoveLayerAttribute(node); + CivlAttributes.RemoveRefinesAttribute(node); + return base.VisitProcedure(node); + } + + public override Implementation VisitImplementation(Implementation node) + { + CivlAttributes.RemoveYieldsAttribute(node); + CivlAttributes.RemoveMoverAttribute(node); + CivlAttributes.RemoveLayerAttribute(node); + CivlAttributes.RemoveRefinesAttribute(node); + return base.VisitImplementation(node); + } + + public override Requires VisitRequires(Requires node) + { + CivlAttributes.RemoveLayerAttribute(node); + return base.VisitRequires(node); + } + + public override Ensures VisitEnsures(Ensures node) + { + CivlAttributes.RemoveLayerAttribute(node); + return base.VisitEnsures(node); + } + + public override Cmd VisitAssertCmd(AssertCmd node) + { + CivlAttributes.RemoveLayerAttribute(node); + return base.VisitAssertCmd(node); + } + + public override Variable VisitVariable(Variable node) + { + CivlAttributes.RemoveLayerAttribute(node); + return base.VisitVariable(node); + } + + public override Function VisitFunction(Function node) + { + CivlAttributes.RemoveWitnessAttribute(node); + return base.VisitFunction(node); } } } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/CivlUtil.cs boogie-2.4.1+dfsg/Source/Concurrency/CivlUtil.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/CivlUtil.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/CivlUtil.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Boogie; + +namespace Microsoft.Boogie +{ + public class CivlUtil + { + public static void AddInlineAttribute(Declaration decl) + { + decl.AddAttribute("inline", Expr.Literal(1)); + } + } + + // Handy syntactic suggar missing in Expr + public static class ExprHelper + { + public static NAryExpr FunctionCall(Function f, params Expr[] args) + { + return new NAryExpr(Token.NoToken, new FunctionCall(f), args); + } + + public static NAryExpr IfThenElse(Expr ifExpr, Expr thenExpr, Expr elseExpr) + { + return new NAryExpr(Token.NoToken, new IfThenElse(Token.NoToken), + new Expr[] { ifExpr, thenExpr, elseExpr }); + } + + public static OldExpr Old(Expr expr) + { + return new OldExpr(Token.NoToken, expr); + } + } + + public static class LinqExtensions + { + public static IEnumerable> CartesianProduct(this IEnumerable> sequences) + { + IEnumerable> emptyProduct = new[] { Enumerable.Empty() }; + return sequences.Aggregate( + emptyProduct, + (accumulator, sequence) => + from acc in accumulator + from item in sequence + select acc.Concat(new[] { item })); + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/CivlVCGeneration.cs boogie-2.4.1+dfsg/Source/Concurrency/CivlVCGeneration.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/CivlVCGeneration.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/CivlVCGeneration.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Boogie +{ + public class CivlVCGeneration + { + public static void Transform(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker) + { + Program program = linearTypeChecker.program; + + // Store the original declarations of yielding procedures, which will be removed after desugaring below. + var origProc = program.TopLevelDeclarations.OfType().Where(p => civlTypeChecker.procToYieldingProc.ContainsKey(p)); + var origImpl = program.TopLevelDeclarations.OfType().Where(i => civlTypeChecker.procToYieldingProc.ContainsKey(i.Proc)); + List originalDecls = Enumerable.Union(origProc, origImpl).ToList(); + + // Commutativity checks + List decls = new List(); + if (!CommandLineOptions.Clo.TrustAtomicityTypes) + { + MoverCheck.AddCheckers(linearTypeChecker, civlTypeChecker, decls); + } + + // Desugaring of yielding procedures + YieldingProcChecker.AddCheckers(linearTypeChecker, civlTypeChecker, decls); + + // Linear type checks + LinearTypeChecker.AddCheckers(linearTypeChecker, civlTypeChecker, decls); + + // Remove original declarations and add new checkers generated above + program.RemoveTopLevelDeclarations(x => originalDecls.Contains(x)); + program.AddTopLevelDeclarations(decls); + + civlTypeChecker.SubstituteBackwardAssignments(); + + foreach (AtomicAction atomicAction in civlTypeChecker.procToAtomicAction.Values) + { + program.RemoveTopLevelDeclaration(atomicAction.proc); + program.RemoveTopLevelDeclaration(atomicAction.impl); + } + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/Concurrency.csproj boogie-2.4.1+dfsg/Source/Concurrency/Concurrency.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/Concurrency.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/Concurrency.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -1,4 +1,4 @@ - + @@ -9,9 +9,9 @@ Properties Concurrency BoogieConcurrency - v4.0 + v4.5 512 - Client + AnyCPU @@ -22,6 +22,7 @@ TRACE;DEBUG prompt 4 + false AnyCPU @@ -31,6 +32,7 @@ TRACE prompt 4 + false @@ -49,6 +51,7 @@ AnyCPU prompt MinimumRecommendedRules.ruleset + false @@ -71,14 +74,22 @@ - + + + - - + + + + + + + + @@ -112,4 +123,4 @@ --> - \ No newline at end of file + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/Concurrency-NetCore.csproj boogie-2.4.1+dfsg/Source/Concurrency/Concurrency-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/Concurrency-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/Concurrency-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,32 @@ + + + + Library + BoogieConcurrency + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + + + + + false + + + + + version.cs + + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/GlobalSnapshotInstrumentation.cs boogie-2.4.1+dfsg/Source/Concurrency/GlobalSnapshotInstrumentation.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/GlobalSnapshotInstrumentation.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/GlobalSnapshotInstrumentation.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,76 @@ +using System.Collections.Generic; + +namespace Microsoft.Boogie +{ + class GlobalSnapshotInstrumentation + { + private Dictionary oldGlobalMap; + private List newLocalVars; + + public GlobalSnapshotInstrumentation(CivlTypeChecker civlTypeChecker) + { + newLocalVars = new List(); + oldGlobalMap = new Dictionary(); + foreach (Variable g in civlTypeChecker.sharedVariables) + { + LocalVariable l = OldGlobalLocal(g); + oldGlobalMap[g] = l; + newLocalVars.Add(l); + } + } + + public Dictionary OldGlobalMap => oldGlobalMap; + + public List NewLocalVars => newLocalVars; + + public List CreateUpdatesToOldGlobalVars() + { + List lhss = new List(); + List rhss = new List(); + foreach (Variable g in oldGlobalMap.Keys) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldGlobalMap[g]))); + rhss.Add(Expr.Ident(g)); + } + var cmds = new List(); + if (lhss.Count > 0) + { + cmds.Add(new AssignCmd(Token.NoToken, lhss, rhss)); + } + return cmds; + } + + public List CreateInitCmds() + { + List lhss = new List(); + List rhss = new List(); + foreach (Variable g in oldGlobalMap.Keys) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldGlobalMap[g]))); + rhss.Add(Expr.Ident(g)); + } + var initCmds = new List(); + if (lhss.Count > 0) + { + initCmds.Add(new AssignCmd(Token.NoToken, lhss, rhss)); + } + return initCmds; + } + + public List CreateAssumeCmds() + { + List newCmds = new List(); + foreach (Variable v in oldGlobalMap.Keys) + { + newCmds.Add(new AssumeCmd(Token.NoToken, Expr.Eq(Expr.Ident(v), Expr.Ident(oldGlobalMap[v])))); + } + return newCmds; + } + + private LocalVariable OldGlobalLocal(Variable v) + { + return new LocalVariable(Token.NoToken, + new TypedIdent(Token.NoToken, $"og_global_old_{v.Name}", v.TypedIdent.Type)); + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/LinearSets.cs boogie-2.4.1+dfsg/Source/Concurrency/LinearSets.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/LinearSets.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/LinearSets.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,1003 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Boogie; -using System.Diagnostics; - -namespace Microsoft.Boogie -{ - public class LinearEraser : ReadOnlyVisitor - { - private QKeyValue RemoveLinearAttribute(QKeyValue iter) - { - if (iter == null) return null; - iter.Next = RemoveLinearAttribute(iter.Next); - return (iter.Key == "linear" || iter.Key == "linear_in" || iter.Key == "linear_out") ? iter.Next : iter; - } - - public override Variable VisitVariable(Variable node) - { - node.Attributes = RemoveLinearAttribute(node.Attributes); - return base.VisitVariable(node); - } - - public override Function VisitFunction(Function node) - { - node.Attributes = RemoveLinearAttribute(node.Attributes); - return base.VisitFunction(node); - } - } - - public enum LinearKind { - LINEAR, - LINEAR_IN, - LINEAR_OUT - } - - public class LinearTypeChecker : ReadOnlyVisitor - { - public Program program; - public int errorCount; - public CheckingContext checkingContext; - public Dictionary> domainNameToCollectors; - private Dictionary> availableLinearVars; - public Dictionary inParamToLinearQualifier; - public Dictionary outParamToDomainName; - public Dictionary varToDomainName; - public Dictionary globalVarToDomainName; - public Dictionary linearDomains; - - public LinearTypeChecker(Program program) - { - this.program = program; - this.errorCount = 0; - this.checkingContext = new CheckingContext(null); - this.domainNameToCollectors = new Dictionary>(); - this.availableLinearVars = new Dictionary>(); - this.inParamToLinearQualifier = new Dictionary(); - this.outParamToDomainName = new Dictionary(); - this.varToDomainName = new Dictionary(); - this.globalVarToDomainName = new Dictionary(); - this.linearDomains = new Dictionary(); - } - public void TypeCheck() - { - this.VisitProgram(program); - foreach (string domainName in domainNameToCollectors.Keys) - { - var collectors = domainNameToCollectors[domainName]; - if (collectors.Count == 0) continue; - this.linearDomains[domainName] = new LinearDomain(program, domainName, collectors); - } - Dictionary> newAvailableLinearVars = new Dictionary>(); - foreach (Absy absy in this.availableLinearVars.Keys) - { - HashSet vars = new HashSet(); - foreach (Variable var in this.availableLinearVars[absy]) - { - if (var is GlobalVariable) continue; - string domainName = FindDomainName(var); - if (this.linearDomains.ContainsKey(domainName)) - { - vars.Add(var); - } - } - newAvailableLinearVars[absy] = vars; - } - this.availableLinearVars = newAvailableLinearVars; - var temp = new Dictionary(); - foreach (Variable v in outParamToDomainName.Keys) - { - if (linearDomains.ContainsKey(outParamToDomainName[v])) - temp[v] = outParamToDomainName[v]; - } - this.outParamToDomainName = temp; - temp = new Dictionary(); - foreach (Variable v in varToDomainName.Keys) - { - if (linearDomains.ContainsKey(varToDomainName[v])) - temp[v] = varToDomainName[v]; - } - this.varToDomainName = temp; - temp = new Dictionary(); - foreach (Variable v in globalVarToDomainName.Keys) - { - if (linearDomains.ContainsKey(globalVarToDomainName[v])) - temp[v] = globalVarToDomainName[v]; - } - this.globalVarToDomainName = temp; - } - private void Error(Absy node, string message) - { - checkingContext.Error(node, message); - errorCount++; - } - public override Program VisitProgram(Program node) - { - foreach (GlobalVariable g in program.GlobalVariables) - { - string domainName = FindDomainName(g); - if (domainName != null) - { - globalVarToDomainName[g] = domainName; - } - } - return base.VisitProgram(node); - } - public override Function VisitFunction(Function node) - { - string domainName = QKeyValue.FindStringAttribute(node.Attributes, "linear"); - if (domainName != null) - { - if (!domainNameToCollectors.ContainsKey(domainName)) - { - domainNameToCollectors[domainName] = new Dictionary(); - } - if (node.InParams.Count == 1 && node.OutParams.Count == 1) - { - Type inType = node.InParams[0].TypedIdent.Type; - MapType outType = node.OutParams[0].TypedIdent.Type as MapType; - if (domainNameToCollectors[domainName].ContainsKey(inType)) - { - Error(node, string.Format("A collector for domain for input type has already been defined")); - } - else if (outType == null || outType.Arguments.Count != 1 || !outType.Result.Equals(Type.Bool)) - { - Error(node, "Output of a linear domain collector should be of set type"); - } - else - { - domainNameToCollectors[domainName][inType] = node; - } - } - else - { - Error(node, "Linear domain collector should have one input and one output parameter"); - } - } - return base.VisitFunction(node); - } - public override Implementation VisitImplementation(Implementation node) - { - node.PruneUnreachableBlocks(); - node.ComputePredecessorsForBlocks(); - GraphUtil.Graph graph = Program.GraphFromImpl(node); - graph.ComputeLoops(); - - HashSet start = new HashSet(globalVarToDomainName.Keys); - for (int i = 0; i < node.InParams.Count; i++) - { - Variable v = node.Proc.InParams[i]; - string domainName = FindDomainName(v); - if (domainName != null) - { - var kind = FindLinearKind(v); - inParamToLinearQualifier[node.InParams[i]] = new LinearQualifier(domainName, kind); - if (kind == LinearKind.LINEAR || kind == LinearKind.LINEAR_IN) - { - start.Add(node.InParams[i]); - } - } - } - for (int i = 0; i < node.OutParams.Count; i++) - { - string domainName = FindDomainName(node.Proc.OutParams[i]); - if (domainName != null) - { - outParamToDomainName[node.OutParams[i]] = domainName; - } - } - - var oldErrorCount = this.errorCount; - var impl = base.VisitImplementation(node); - if (oldErrorCount < this.errorCount) - return impl; - - Stack dfsStack = new Stack(); - HashSet dfsStackAsSet = new HashSet(); - availableLinearVars[node.Blocks[0]] = start; - dfsStack.Push(node.Blocks[0]); - dfsStackAsSet.Add(node.Blocks[0]); - while (dfsStack.Count > 0) - { - Block b = dfsStack.Pop(); - dfsStackAsSet.Remove(b); - HashSet end = PropagateAvailableLinearVarsAcrossBlock(b); - if (b.TransferCmd is ReturnCmd) - { - foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(end)) - { - Error(b.TransferCmd, string.Format("Global variable {0} must be available at a return", g.Name)); - } - foreach (Variable v in node.InParams) - { - if (FindDomainName(v) == null || FindLinearKind(v) == LinearKind.LINEAR_IN || end.Contains(v)) continue; - Error(b.TransferCmd, string.Format("Input variable {0} must be available at a return", v.Name)); - } - foreach (Variable v in node.OutParams) - { - if (FindDomainName(v) == null || end.Contains(v)) continue; - Error(b.TransferCmd, string.Format("Output variable {0} must be available at a return", v.Name)); - } - continue; - } - GotoCmd gotoCmd = b.TransferCmd as GotoCmd; - foreach (Block target in gotoCmd.labelTargets) - { - if (!availableLinearVars.ContainsKey(target)) - { - availableLinearVars[target] = new HashSet(end); - dfsStack.Push(target); - dfsStackAsSet.Add(target); - } - else - { - var savedAvailableVars = new HashSet(availableLinearVars[target]); - availableLinearVars[target].IntersectWith(end); - if (savedAvailableVars.IsProperSupersetOf(availableLinearVars[target]) && !dfsStackAsSet.Contains(target)) - { - dfsStack.Push(target); - dfsStackAsSet.Add(target); - } - } - } - } - - if (graph.Reducible) - { - foreach (Block header in graph.Headers) - { - foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(availableLinearVars[header])) - { - Error(header, string.Format("Global variable {0} must be available at a loop head", g.Name)); - } - } - } - return impl; - } - public void AddAvailableVars(CallCmd callCmd, HashSet start) - { - foreach (IdentifierExpr ie in callCmd.Outs) - { - if (FindDomainName(ie.Decl) == null) continue; - start.Add(ie.Decl); - } - for (int i = 0; i < callCmd.Proc.InParams.Count; i++) - { - IdentifierExpr ie = callCmd.Ins[i] as IdentifierExpr; - if (ie == null) continue; - Variable v = callCmd.Proc.InParams[i]; - if (FindDomainName(v) == null) continue; - if (FindLinearKind(v) == LinearKind.LINEAR_OUT) - { - start.Add(ie.Decl); - } - } - } - public void AddAvailableVars(ParCallCmd parCallCmd, HashSet start) - { - foreach (CallCmd callCmd in parCallCmd.CallCmds) - { - AddAvailableVars(callCmd, start); - } - } - private HashSet PropagateAvailableLinearVarsAcrossBlock(Block b) { - HashSet start = new HashSet(availableLinearVars[b]); - foreach (Cmd cmd in b.Cmds) - { - if (cmd is AssignCmd) - { - AssignCmd assignCmd = (AssignCmd)cmd; - for (int i = 0; i < assignCmd.Lhss.Count; i++) - { - if (FindDomainName(assignCmd.Lhss[i].DeepAssignedVariable) == null) continue; - IdentifierExpr ie = assignCmd.Rhss[i] as IdentifierExpr; - if (!start.Contains(ie.Decl)) - { - Error(ie, "unavailable source for a linear read"); - } - else - { - start.Remove(ie.Decl); - } - } - foreach (AssignLhs assignLhs in assignCmd.Lhss) - { - if (FindDomainName(assignLhs.DeepAssignedVariable) == null) continue; - start.Add(assignLhs.DeepAssignedVariable); - } - } - else if (cmd is CallCmd) - { - foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(start)) - { - Error(cmd, string.Format("Global variable {0} must be available at a call", g.Name)); - } - CallCmd callCmd = (CallCmd)cmd; - for (int i = 0; i < callCmd.Proc.InParams.Count; i++) - { - Variable param = callCmd.Proc.InParams[i]; - if (FindDomainName(param) == null) continue; - IdentifierExpr ie = callCmd.Ins[i] as IdentifierExpr; - LinearKind paramKind = FindLinearKind(param); - if (start.Contains(ie.Decl)) - { - if (callCmd.IsAsync || paramKind == LinearKind.LINEAR_IN) - { - start.Remove(ie.Decl); - } - } - else - { - if (paramKind == LinearKind.LINEAR_OUT) - { - start.Add(ie.Decl); - } - else - { - Error(ie, "unavailable source for a linear read"); - } - } - } - availableLinearVars[callCmd] = new HashSet(start); - AddAvailableVars(callCmd, start); - } - else if (cmd is ParCallCmd) - { - foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(start)) - { - Error(cmd, string.Format("Global variable {0} must be available at a call", g.Name)); - } - ParCallCmd parCallCmd = (ParCallCmd)cmd; - foreach (CallCmd callCmd in parCallCmd.CallCmds) - { - for (int i = 0; i < callCmd.Proc.InParams.Count; i++) - { - Variable param = callCmd.Proc.InParams[i]; - if (FindDomainName(param) == null) continue; - IdentifierExpr ie = callCmd.Ins[i] as IdentifierExpr; - LinearKind paramKind = FindLinearKind(param); - if (start.Contains(ie.Decl)) - { - if (paramKind == LinearKind.LINEAR_IN) - { - start.Remove(ie.Decl); - } - } - else - { - if (paramKind == LinearKind.LINEAR_OUT) - { - start.Add(ie.Decl); - } - else - { - Error(ie, "unavailable source for a linear read"); - } - } - } - } - availableLinearVars[parCallCmd] = new HashSet(start); - AddAvailableVars(parCallCmd, start); - } - else if (cmd is HavocCmd) - { - HavocCmd havocCmd = (HavocCmd)cmd; - foreach (IdentifierExpr ie in havocCmd.Vars) - { - if (FindDomainName(ie.Decl) == null) continue; - start.Remove(ie.Decl); - } - } - else if (cmd is YieldCmd) - { - foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(start)) - { - Error(cmd, string.Format("Global variable {0} must be available at a yield", g.Name)); - } - availableLinearVars[cmd] = new HashSet(start); - } - } - return start; - } - public string FindDomainName(Variable v) - { - if (globalVarToDomainName.ContainsKey(v)) - return globalVarToDomainName[v]; - if (inParamToLinearQualifier.ContainsKey(v)) - return inParamToLinearQualifier[v].domainName; - if (outParamToDomainName.ContainsKey(v)) - return outParamToDomainName[v]; - string domainName = QKeyValue.FindStringAttribute(v.Attributes, "linear"); - if (domainName != null) - return domainName; - domainName = QKeyValue.FindStringAttribute(v.Attributes, "linear_in"); - if (domainName != null) - return domainName; - return QKeyValue.FindStringAttribute(v.Attributes, "linear_out"); - } - public LinearKind FindLinearKind(Variable v) - { - if (globalVarToDomainName.ContainsKey(v)) - return LinearKind.LINEAR; - if (inParamToLinearQualifier.ContainsKey(v)) - return inParamToLinearQualifier[v].kind; - if (outParamToDomainName.ContainsKey(v)) - return LinearKind.LINEAR; - - if (QKeyValue.FindStringAttribute(v.Attributes, "linear") != null) - { - return LinearKind.LINEAR; - } - else if (QKeyValue.FindStringAttribute(v.Attributes, "linear_in") != null) - { - return LinearKind.LINEAR_IN; - } - else if (QKeyValue.FindStringAttribute(v.Attributes, "linear_out") != null) - { - return LinearKind.LINEAR_OUT; - } - else - { - Debug.Assert(false); - return LinearKind.LINEAR; - } - } - public override Variable VisitVariable(Variable node) - { - string domainName = FindDomainName(node); - if (domainName != null) - { - if (!domainNameToCollectors.ContainsKey(domainName)) - { - domainNameToCollectors[domainName] = new Dictionary(); - } - LinearKind kind = FindLinearKind(node); - if (kind != LinearKind.LINEAR) - { - if (node is GlobalVariable || node is LocalVariable || (node is Formal && !(node as Formal).InComing)) - { - Error(node, "Variable must be declared linear (as opposed to linear_in or linear_out)"); - } - } - } - return base.VisitVariable(node); - } - public override Cmd VisitAssignCmd(AssignCmd node) - { - HashSet rhsVars = new HashSet(); - for (int i = 0; i < node.Lhss.Count; i++) - { - AssignLhs lhs = node.Lhss[i]; - Variable lhsVar = lhs.DeepAssignedVariable; - string domainName = FindDomainName(lhsVar); - if (domainName == null) continue; - SimpleAssignLhs salhs = lhs as SimpleAssignLhs; - if (salhs == null) - { - Error(node, string.Format("Only simple assignment allowed on linear variable {0}", lhsVar.Name)); - continue; - } - IdentifierExpr rhs = node.Rhss[i] as IdentifierExpr; - if (rhs == null) - { - Error(node, string.Format("Only variable can be assigned to linear variable {0}", lhsVar.Name)); - continue; - } - string rhsDomainName = FindDomainName(rhs.Decl); - if (rhsDomainName == null) - { - Error(node, string.Format("Only linear variable can be assigned to linear variable {0}", lhsVar.Name)); - continue; - } - if (domainName != rhsDomainName) - { - Error(node, string.Format("Linear variable of domain {0} cannot be assigned to linear variable of domain {1}", rhsDomainName, domainName)); - continue; - } - if (rhsVars.Contains(rhs.Decl)) - { - Error(node, string.Format("Linear variable {0} can occur only once in the right-hand-side of an assignment", rhs.Decl.Name)); - continue; - } - rhsVars.Add(rhs.Decl); - } - return base.VisitAssignCmd(node); - } - public override Cmd VisitCallCmd(CallCmd node) - { - HashSet inVars = new HashSet(); - for (int i = 0; i < node.Proc.InParams.Count; i++) - { - Variable formal = node.Proc.InParams[i]; - string domainName = FindDomainName(formal); - if (domainName == null) continue; - IdentifierExpr actual = node.Ins[i] as IdentifierExpr; - if (actual == null) - { - Error(node, string.Format("Only variable can be passed to linear parameter {0}", formal.Name)); - continue; - } - string actualDomainName = FindDomainName(actual.Decl); - if (actualDomainName == null) - { - Error(node, string.Format("Only a linear argument can be passed to linear parameter {0}", formal.Name)); - continue; - } - if (domainName != actualDomainName) - { - Error(node, "The domains of formal and actual parameters must be the same"); - continue; - } - if (actual.Decl is GlobalVariable) - { - Error(node, "Only local linear variable can be an actual input parameter of a procedure call"); - continue; - } - if (inVars.Contains(actual.Decl)) - { - Error(node, string.Format("Linear variable {0} can occur only once as an input parameter", actual.Decl.Name)); - continue; - } - inVars.Add(actual.Decl); - } - for (int i = 0; i < node.Proc.OutParams.Count; i++) - { - IdentifierExpr actual = node.Outs[i]; - string actualDomainName = FindDomainName(actual.Decl); - if (actualDomainName == null) continue; - Variable formal = node.Proc.OutParams[i]; - string domainName = FindDomainName(formal); - if (domainName == null) - { - Error(node, "Only a linear variable can be passed to a linear parameter"); - continue; - } - if (domainName != actualDomainName) - { - Error(node, "The domains of formal and actual parameters must be the same"); - continue; - } - if (actual.Decl is GlobalVariable) - { - Error(node, "Only local linear variable can be actual output parameter of a procedure call"); - continue; - } - } - return base.VisitCallCmd(node); - } - public override Cmd VisitParCallCmd(ParCallCmd node) - { - HashSet parallelCallInvars = new HashSet(); - foreach (CallCmd callCmd in node.CallCmds) - { - for (int i = 0; i < callCmd.Proc.InParams.Count; i++) - { - Variable formal = callCmd.Proc.InParams[i]; - string domainName = FindDomainName(formal); - if (domainName == null) continue; - IdentifierExpr actual = callCmd.Ins[i] as IdentifierExpr; - if (parallelCallInvars.Contains(actual.Decl)) - { - Error(node, string.Format("Linear variable {0} can occur only once as an input parameter of a parallel call", actual.Decl.Name)); - } - else - { - parallelCallInvars.Add(actual.Decl); - } - } - } - return base.VisitParCallCmd(node); - } - - public override Requires VisitRequires(Requires requires) - { - return requires; - } - - public override Ensures VisitEnsures(Ensures ensures) - { - return ensures; - } - - public IEnumerable AvailableLinearVars(Absy absy) - { - if (availableLinearVars.ContainsKey(absy)) - { - return availableLinearVars[absy]; - } - else - { - return new HashSet(); - } - } - - private void AddDisjointnessExpr(List newCmds, Absy absy, Dictionary domainNameToInputVar) - { - Dictionary> domainNameToScope = new Dictionary>(); - foreach (var domainName in linearDomains.Keys) - { - domainNameToScope[domainName] = new HashSet(); - } - foreach (Variable v in AvailableLinearVars(absy)) - { - var domainName = FindDomainName(v); - domainNameToScope[domainName].Add(v); - } - foreach (Variable v in globalVarToDomainName.Keys) - { - var domainName = FindDomainName(v); - domainNameToScope[domainName].Add(v); - } - foreach (string domainName in linearDomains.Keys) - { - newCmds.Add(new AssumeCmd(Token.NoToken, DisjointnessExpr(domainName, domainNameToInputVar[domainName], domainNameToScope[domainName]))); - } - } - - public void Transform() - { - foreach (var impl in program.Implementations) - { - Dictionary domainNameToInputVar = new Dictionary(); - foreach (string domainName in linearDomains.Keys) - { - var domain = linearDomains[domainName]; - Formal f = new Formal( - Token.NoToken, - new TypedIdent(Token.NoToken, - "linear_" + domainName + "_in", - new MapType(Token.NoToken, new List(), - new List { domain.elementType }, Type.Bool)), true); - impl.InParams.Add(f); - domainNameToInputVar[domainName] = f; - } - - foreach (Block b in impl.Blocks) - { - List newCmds = new List(); - for (int i = 0; i < b.Cmds.Count; i++) - { - Cmd cmd = b.Cmds[i]; - newCmds.Add(cmd); - if (cmd is CallCmd) - { - CallCmd callCmd = cmd as CallCmd; - if (callCmd.IsAsync) - { - foreach (var domainName in linearDomains.Keys) - { - var domain = linearDomains[domainName]; - var expr = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapConstBool), new List { Expr.False }); - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); - callCmd.Ins.Add(expr); - } - } - else - { - Dictionary domainNameToExpr = new Dictionary(); - foreach (var domainName in linearDomains.Keys) - { - domainNameToExpr[domainName] = Expr.Ident(domainNameToInputVar[domainName]); - } - foreach (Variable v in AvailableLinearVars(callCmd)) - { - var domainName = FindDomainName(v); - var domain = linearDomains[domainName]; - if (!domain.collectors.ContainsKey(v.TypedIdent.Type)) continue; - Expr ie = new NAryExpr(Token.NoToken, new FunctionCall(domain.collectors[v.TypedIdent.Type]), new List { Expr.Ident(v) }); - var expr = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapOrBool), new List { ie, domainNameToExpr[domainName] }); - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); - domainNameToExpr[domainName] = expr; - } - foreach (var domainName in linearDomains.Keys) - { - callCmd.Ins.Add(domainNameToExpr[domainName]); - } - } - } - else if (cmd is ParCallCmd) - { - ParCallCmd parCallCmd = (ParCallCmd)cmd; - foreach (CallCmd callCmd in parCallCmd.CallCmds) - { - foreach (var domainName in linearDomains.Keys) - { - var domain = linearDomains[domainName]; - var expr = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapConstBool), new List { Expr.False }); - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); - callCmd.Ins.Add(expr); - } - } - } - else if (cmd is YieldCmd) - { - AddDisjointnessExpr(newCmds, cmd, domainNameToInputVar); - } - } - b.Cmds = newCmds; - } - - { - // Loops - impl.PruneUnreachableBlocks(); - impl.ComputePredecessorsForBlocks(); - GraphUtil.Graph g = Program.GraphFromImpl(impl); - g.ComputeLoops(); - if (g.Reducible) - { - foreach (Block header in g.Headers) - { - List newCmds = new List(); - AddDisjointnessExpr(newCmds, header, domainNameToInputVar); - newCmds.AddRange(header.Cmds); - header.Cmds = newCmds; - } - } - } - } - - foreach (var proc in program.Procedures) - { - Dictionary> domainNameToInputScope = new Dictionary>(); - Dictionary> domainNameToOutputScope = new Dictionary>(); - foreach (var domainName in linearDomains.Keys) - { - domainNameToInputScope[domainName] = new HashSet(); - domainNameToOutputScope[domainName] = new HashSet(); - - } - foreach (Variable v in globalVarToDomainName.Keys) - { - var domainName = globalVarToDomainName[v]; - domainNameToInputScope[domainName].Add(v); - domainNameToOutputScope[domainName].Add(v); - } - foreach (Variable v in proc.InParams) - { - var domainName = FindDomainName(v); - if (domainName == null) continue; - if (!this.linearDomains.ContainsKey(domainName)) continue; - domainNameToInputScope[domainName].Add(v); - } - foreach (Variable v in proc.OutParams) - { - var domainName = FindDomainName(v); - if (domainName == null) continue; - if (!this.linearDomains.ContainsKey(domainName)) continue; - domainNameToOutputScope[domainName].Add(v); - } - foreach (var domainName in linearDomains.Keys) - { - proc.Requires.Add(new Requires(true, DisjointnessExpr(domainName, domainNameToInputScope[domainName]))); - var domain = linearDomains[domainName]; - Formal f = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_in", new MapType(Token.NoToken, new List(), new List { domain.elementType }, Type.Bool)), true); - proc.InParams.Add(f); - proc.Ensures.Add(new Ensures(true, DisjointnessExpr(domainName, f, domainNameToOutputScope[domainName]))); - } - } - - foreach (LinearDomain domain in linearDomains.Values) - { - program.AddTopLevelDeclaration(domain.mapConstBool); - program.AddTopLevelDeclaration(domain.mapConstInt); - program.AddTopLevelDeclaration(domain.mapEqInt); - program.AddTopLevelDeclaration(domain.mapImpBool); - program.AddTopLevelDeclaration(domain.mapOrBool); - foreach (Axiom axiom in domain.axioms) - { - program.AddTopLevelDeclaration(axiom); - } - } - - //int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured; - //CommandLineOptions.Clo.PrintUnstructured = 1; - //PrintBplFile("lsd.bpl", program, false, false); - //CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; - } - - private Expr SubsetExpr(LinearDomain domain, Expr ie, Variable partition, int partitionCount) - { - Expr e = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapConstInt), new List { new LiteralExpr(Token.NoToken, Microsoft.Basetypes.BigNum.FromInt(partitionCount)) }); - e = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapEqInt), new List { Expr.Ident(partition), e }); - e = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapImpBool), new List { ie, e }); - e = Expr.Eq(e, new NAryExpr(Token.NoToken, new FunctionCall(domain.mapConstBool), new List { Expr.True })); - return e; - } - - private Expr SubsetExprs(LinearDomain domain, HashSet scope, Variable partition, int count, Expr expr) - { - foreach (Variable v in scope) - { - if (!domain.collectors.ContainsKey(v.TypedIdent.Type)) continue; - Expr ie = new NAryExpr(Token.NoToken, new FunctionCall(domain.collectors[v.TypedIdent.Type]), new List { Expr.Ident(v) }); - expr = Expr.And(SubsetExpr(domain, ie, partition, count), expr); - count++; - } - expr = new ExistsExpr(Token.NoToken, new List { partition }, expr); - expr.Resolve(new ResolutionContext(null)); - expr.Typecheck(new TypecheckingContext(null)); - return expr; - } - - public Expr DisjointnessExpr(string domainName, Variable inputVar, HashSet scope) - { - LinearDomain domain = linearDomains[domainName]; - BoundVariable partition = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("partition_{0}", domainName), new MapType(Token.NoToken, new List(), new List { domain.elementType }, Microsoft.Boogie.Type.Int))); - return SubsetExprs(domain, scope, partition, 1, SubsetExpr(domain, Expr.Ident(inputVar), partition, 0)); - } - - public Expr DisjointnessExpr(string domainName, HashSet scope) - { - LinearDomain domain = linearDomains[domainName]; - BoundVariable partition = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("partition_{0}", domainName), new MapType(Token.NoToken, new List(), new List { domain.elementType }, Microsoft.Boogie.Type.Int))); - return SubsetExprs(domain, scope, partition, 0, Expr.True); - } - } - - public class LinearQualifier - { - public string domainName; - public LinearKind kind; - public LinearQualifier(string domainName, LinearKind kind) - { - this.domainName = domainName; - this.kind = kind; - } - } - - public class LinearDomain - { - public Function mapEqInt; - public Function mapConstInt; - public Function mapOrBool; - public Function mapImpBool; - public Function mapConstBool; - public List axioms; - public Type elementType; - public Dictionary collectors; - - public LinearDomain(Program program, string domainName, Dictionary collectors) - { - this.axioms = new List(); - this.collectors = collectors; - MapType setType = (MapType)collectors.First().Value.OutParams[0].TypedIdent.Type; - this.elementType = setType.Arguments[0]; - MapType mapTypeBool = new MapType(Token.NoToken, new List(), new List { this.elementType }, Type.Bool); - MapType mapTypeInt = new MapType(Token.NoToken, new List(), new List { this.elementType }, Type.Int); - this.mapOrBool = new Function(Token.NoToken, "linear_" + domainName + "_MapOr", - new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeBool), true), - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeBool), true) }, - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeBool), false)); - if (CommandLineOptions.Clo.UseArrayTheory) - { - this.mapOrBool.AddAttribute("builtin", "MapOr"); - } - else - { - BoundVariable a = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeBool)); - IdentifierExpr aie = Expr.Ident(a); - BoundVariable b = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeBool)); - IdentifierExpr bie = Expr.Ident(b); - BoundVariable x = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "x", elementType)); - IdentifierExpr xie = Expr.Ident(x); - var mapApplTerm = new NAryExpr(Token.NoToken, new FunctionCall(mapOrBool), new List { aie, bie } ); - var lhsTerm = new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { mapApplTerm, xie } ); - var rhsTerm = Expr.Or(new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { aie, xie } ), - new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { bie, xie} )); - var axiomExpr = new ForallExpr(Token.NoToken, new List(), new List { a, b }, null, - new Trigger(Token.NoToken, true, new List { mapApplTerm }), - new ForallExpr(Token.NoToken, new List { x }, Expr.Binary(BinaryOperator.Opcode.Eq, lhsTerm, rhsTerm))); - axiomExpr.Typecheck(new TypecheckingContext(null)); - axioms.Add(new Axiom(Token.NoToken, axiomExpr)); - } - - this.mapImpBool = new Function(Token.NoToken, "linear_" + domainName + "_MapImp", - new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeBool), true), - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeBool), true) }, - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeBool), false)); - if (CommandLineOptions.Clo.UseArrayTheory) - { - this.mapImpBool.AddAttribute("builtin", "MapImp"); - } - else - { - BoundVariable a = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeBool)); - IdentifierExpr aie = Expr.Ident(a); - BoundVariable b = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeBool)); - IdentifierExpr bie = Expr.Ident(b); - BoundVariable x = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "x", elementType)); - IdentifierExpr xie = Expr.Ident(x); - var mapApplTerm = new NAryExpr(Token.NoToken, new FunctionCall(mapImpBool), new List { aie, bie }); - var lhsTerm = new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { mapApplTerm, xie }); - var rhsTerm = Expr.Imp(new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { aie, xie }), - new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { bie, xie })); - var axiomExpr = new ForallExpr(Token.NoToken, new List(), new List { a, b }, null, - new Trigger(Token.NoToken, true, new List { mapApplTerm }), - new ForallExpr(Token.NoToken, new List { x }, Expr.Binary(BinaryOperator.Opcode.Eq, lhsTerm, rhsTerm))); - axiomExpr.Typecheck(new TypecheckingContext(null)); - axioms.Add(new Axiom(Token.NoToken, axiomExpr)); - } - - this.mapConstBool = new Function(Token.NoToken, "linear_" + domainName + "_MapConstBool", - new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", Type.Bool), true) }, - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeBool), false)); - if (CommandLineOptions.Clo.UseArrayTheory) - { - this.mapConstBool.AddAttribute("builtin", "MapConst"); - } - else - { - BoundVariable x = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "x", elementType)); - IdentifierExpr xie = Expr.Ident(x); - var trueTerm = new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), - new List { new NAryExpr(Token.NoToken, new FunctionCall(mapConstBool), new List { Expr.True }), xie }); - var trueAxiomExpr = new ForallExpr(Token.NoToken, new List { x }, trueTerm); - trueAxiomExpr.Typecheck(new TypecheckingContext(null)); - axioms.Add(new Axiom(Token.NoToken, trueAxiomExpr)); - var falseTerm = new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), - new List { new NAryExpr(Token.NoToken, new FunctionCall(mapConstBool), new List { Expr.False }), xie }); - var falseAxiomExpr = new ForallExpr(Token.NoToken, new List { x }, Expr.Unary(Token.NoToken, UnaryOperator.Opcode.Not, falseTerm)); - falseAxiomExpr.Typecheck(new TypecheckingContext(null)); - axioms.Add(new Axiom(Token.NoToken, falseAxiomExpr)); - } - - this.mapEqInt = new Function(Token.NoToken, "linear_" + domainName + "_MapEq", - new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeInt), true), - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeInt), true) }, - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeBool), false)); - if (CommandLineOptions.Clo.UseArrayTheory) - { - this.mapEqInt.AddAttribute("builtin", "MapEq"); - } - else - { - BoundVariable a = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeInt)); - IdentifierExpr aie = Expr.Ident(a); - BoundVariable b = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeInt)); - IdentifierExpr bie = Expr.Ident(b); - BoundVariable x = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "x", elementType)); - IdentifierExpr xie = Expr.Ident(x); - var mapApplTerm = new NAryExpr(Token.NoToken, new FunctionCall(mapEqInt), new List { aie, bie }); - var lhsTerm = new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { mapApplTerm, xie }); - var rhsTerm = Expr.Eq(new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { aie, xie }), - new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { bie, xie })); - var axiomExpr = new ForallExpr(Token.NoToken, new List(), new List { a, b }, null, - new Trigger(Token.NoToken, true, new List { mapApplTerm }), - new ForallExpr(Token.NoToken, new List { x }, Expr.Binary(BinaryOperator.Opcode.Eq, lhsTerm, rhsTerm))); - axiomExpr.Typecheck(new TypecheckingContext(null)); - axioms.Add(new Axiom(Token.NoToken, axiomExpr)); - } - - this.mapConstInt = new Function(Token.NoToken, "linear_" + domainName + "_MapConstInt", - new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", Type.Int), true) }, - new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeInt), false)); - if (CommandLineOptions.Clo.UseArrayTheory) - { - this.mapConstInt.AddAttribute("builtin", "MapConst"); - } - else - { - BoundVariable a = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "a", Type.Int)); - IdentifierExpr aie = Expr.Ident(a); - BoundVariable x = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "x", elementType)); - IdentifierExpr xie = Expr.Ident(x); - var lhsTerm = new NAryExpr(Token.NoToken, new MapSelect(Token.NoToken, 1), new List { new NAryExpr(Token.NoToken, new FunctionCall(mapConstInt), new List { aie }), xie }); - var axiomExpr = new ForallExpr(Token.NoToken, new List { a, x }, Expr.Binary(BinaryOperator.Opcode.Eq, lhsTerm, aie)); - axiomExpr.Typecheck(new TypecheckingContext(null)); - axioms.Add(new Axiom(Token.NoToken, axiomExpr)); - } - - foreach (var axiom in axioms) - { - axiom.Expr.Resolve(new ResolutionContext(null)); - axiom.Expr.Typecheck(new TypecheckingContext(null)); - } - } - } -} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/LinearTypeChecker.cs boogie-2.4.1+dfsg/Source/Concurrency/LinearTypeChecker.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/LinearTypeChecker.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/LinearTypeChecker.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,1195 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Boogie +{ + public enum LinearKind + { + ORDINARY, + LINEAR, + LINEAR_IN, + LINEAR_OUT + } + + public class LinearQualifier + { + public string domainName; + public LinearKind kind; + public LinearQualifier(string domainName, LinearKind kind) + { + this.domainName = domainName; + this.kind = kind; + } + } + + public class LinearDomain + { + public string domainName; + public Function mapConstBool; + public Function mapConstInt; + public Function mapOrBool; + public Function mapImpBool; + public Function mapEqInt; + public Function mapAddInt; + public Function mapIteInt; + public Function mapLeInt; + public List axioms; + public Type elementType; + public MapType mapTypeBool; + public MapType mapTypeInt; + public Dictionary collectors; + + public LinearDomain(Program program, string domainName, Dictionary collectors) + { + this.domainName = domainName; + this.collectors = collectors; + this.axioms = new List(); + + MapType setType = (MapType)collectors.First().Value.OutParams[0].TypedIdent.Type; + this.elementType = setType.Arguments[0]; + this.mapTypeBool = new MapType(Token.NoToken, new List(), new List { this.elementType }, Type.Bool); + this.mapTypeInt = new MapType(Token.NoToken, new List(), new List { this.elementType }, Type.Int); + + MapConstBool(); + MapConstInt(); + MapOrBool(); + MapImpBool(); + MapEqInt(); + MapAddInt(); + MapIteInt(); + MapLeInt(); + + foreach (var axiom in axioms) + { + axiom.Expr.Resolve(new ResolutionContext(null)); + axiom.Expr.Typecheck(new TypecheckingContext(null)); + } + } + + private struct AxiomVariable + { + public readonly BoundVariable Bound; + public readonly IdentifierExpr Ident; + public AxiomVariable(string name, Type type) + { + Bound = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, name, type)); + Ident = Expr.Ident(Bound); + } + } + + private void MapConstBool() + { + this.mapConstBool = new Function(Token.NoToken, "linear_" + domainName + "_MapConstBool", + new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", Type.Bool), true) }, + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeBool), false)); + if (CommandLineOptions.Clo.UseArrayTheory) + { + this.mapConstBool.AddAttribute("builtin", "MapConst"); + } + else + { + AxiomVariable x = new AxiomVariable("x", elementType); + var trueTerm = Expr.Select(ExprHelper.FunctionCall(mapConstBool, Expr.True), x.Ident); + var trueAxiomExpr = new ForallExpr(Token.NoToken, new List { x.Bound }, trueTerm); + trueAxiomExpr.Typecheck(new TypecheckingContext(null)); + axioms.Add(new Axiom(Token.NoToken, trueAxiomExpr)); + var falseTerm = Expr.Select(ExprHelper.FunctionCall(mapConstBool, Expr.False), x.Ident); + var falseAxiomExpr = new ForallExpr(Token.NoToken, new List { x.Bound }, Expr.Unary(Token.NoToken, UnaryOperator.Opcode.Not, falseTerm)); + falseAxiomExpr.Typecheck(new TypecheckingContext(null)); + axioms.Add(new Axiom(Token.NoToken, falseAxiomExpr)); + } + } + + private void MapConstInt() + { + this.mapConstInt = new Function(Token.NoToken, "linear_" + domainName + "_MapConstInt", + new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", Type.Int), true) }, + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeInt), false)); + if (CommandLineOptions.Clo.UseArrayTheory) + { + this.mapConstInt.AddAttribute("builtin", "MapConst"); + } + else + { + AxiomVariable a = new AxiomVariable("a", Type.Int); + AxiomVariable x = new AxiomVariable("x", elementType); + var lhsTerm = Expr.Select(ExprHelper.FunctionCall(mapConstInt, a.Ident), x.Ident); + var axiomExpr = new ForallExpr(Token.NoToken, new List { a.Bound, x.Bound }, Expr.Eq(lhsTerm, a.Ident)); + axiomExpr.Typecheck(new TypecheckingContext(null)); + axioms.Add(new Axiom(Token.NoToken, axiomExpr)); + } + } + + private void MapOrBool() + { + this.mapOrBool = new Function(Token.NoToken, "linear_" + domainName + "_MapOr", + new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeBool), true), + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeBool), true) }, + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeBool), false)); + if (CommandLineOptions.Clo.UseArrayTheory) + { + this.mapOrBool.AddAttribute("builtin", "MapOr"); + } + else + { + AxiomVariable a = new AxiomVariable("a", mapTypeBool); + AxiomVariable b = new AxiomVariable("b", mapTypeBool); + AxiomVariable x = new AxiomVariable("x", elementType); + var mapApplTerm = ExprHelper.FunctionCall(mapOrBool, a.Ident, b.Ident); + var lhsTerm = Expr.Select(mapApplTerm, x.Ident); + var rhsTerm = Expr.Or(Expr.Select(a.Ident, x.Ident), Expr.Select(b.Ident, x.Ident)); + var axiomExpr = new ForallExpr(Token.NoToken, new List(), new List { a.Bound, b.Bound }, null, + new Trigger(Token.NoToken, true, new List { mapApplTerm }), + new ForallExpr(Token.NoToken, new List { x.Bound }, Expr.Eq(lhsTerm, rhsTerm))); + axiomExpr.Typecheck(new TypecheckingContext(null)); + axioms.Add(new Axiom(Token.NoToken, axiomExpr)); + } + } + + private void MapImpBool() + { + this.mapImpBool = new Function(Token.NoToken, "linear_" + domainName + "_MapImp", + new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeBool), true), + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeBool), true) }, + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeBool), false)); + if (CommandLineOptions.Clo.UseArrayTheory) + { + this.mapImpBool.AddAttribute("builtin", "MapImp"); + } + else + { + AxiomVariable a = new AxiomVariable("a", mapTypeBool); + AxiomVariable b = new AxiomVariable("b", mapTypeBool); + AxiomVariable x = new AxiomVariable("x", elementType); + var mapApplTerm = ExprHelper.FunctionCall(mapImpBool, a.Ident, b.Ident); + var lhsTerm = Expr.Select(mapApplTerm, x.Ident); + var rhsTerm = Expr.Imp(Expr.Select(a.Ident, x.Ident), Expr.Select(b.Ident, x.Ident)); + var axiomExpr = new ForallExpr(Token.NoToken, new List(), new List { a.Bound, b.Bound }, null, + new Trigger(Token.NoToken, true, new List { mapApplTerm }), + new ForallExpr(Token.NoToken, new List { x.Bound }, Expr.Eq(lhsTerm, rhsTerm))); + axiomExpr.Typecheck(new TypecheckingContext(null)); + axioms.Add(new Axiom(Token.NoToken, axiomExpr)); + } + } + + private void MapEqInt() + { + this.mapEqInt = new Function(Token.NoToken, "linear_" + domainName + "_MapEq", + new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeInt), true), + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeInt), true) }, + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeBool), false)); + if (CommandLineOptions.Clo.UseArrayTheory) + { + this.mapEqInt.AddAttribute("builtin", "MapEq"); + } + else + { + AxiomVariable a = new AxiomVariable("a", mapTypeInt); + AxiomVariable b = new AxiomVariable("b", mapTypeInt); + AxiomVariable x = new AxiomVariable("x", elementType); + var mapApplTerm = ExprHelper.FunctionCall(mapEqInt, a.Ident, b.Ident); + var lhsTerm = Expr.Select(mapApplTerm, x.Ident); + var rhsTerm = Expr.Eq(Expr.Select(a.Ident, x.Ident), Expr.Select(b.Ident, x.Ident)); + var axiomExpr = new ForallExpr(Token.NoToken, new List(), new List { a.Bound, b.Bound }, null, + new Trigger(Token.NoToken, true, new List { mapApplTerm }), + new ForallExpr(Token.NoToken, new List { x.Bound }, Expr.Eq(lhsTerm, rhsTerm))); + axiomExpr.Typecheck(new TypecheckingContext(null)); + axioms.Add(new Axiom(Token.NoToken, axiomExpr)); + } + } + + private void MapAddInt() + { + this.mapAddInt = new Function(Token.NoToken, "linear_" + domainName + "_MapAdd", + new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeInt), true), + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeInt), true) }, + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeInt), false)); + if (CommandLineOptions.Clo.UseArrayTheory) + { + this.mapAddInt.AddAttribute("builtin", "MapAdd"); + } + else + { + AxiomVariable a = new AxiomVariable("a", mapTypeInt); + AxiomVariable b = new AxiomVariable("b", mapTypeInt); + AxiomVariable x = new AxiomVariable("x", elementType); + var mapApplTerm = ExprHelper.FunctionCall(mapAddInt, a.Ident, b.Ident); + var lhsTerm = Expr.Select(mapApplTerm, x.Ident); + var rhsTerm = Expr.Add(Expr.Select(a.Ident, x.Ident), Expr.Select(b.Ident, x.Ident)); + var axiomExpr = new ForallExpr(Token.NoToken, new List(), new List { a.Bound, b.Bound }, null, + new Trigger(Token.NoToken, true, new List { mapApplTerm }), + new ForallExpr(Token.NoToken, new List { x.Bound }, Expr.Eq(lhsTerm, rhsTerm))); + axiomExpr.Typecheck(new TypecheckingContext(null)); + axioms.Add(new Axiom(Token.NoToken, axiomExpr)); + } + } + + private void MapIteInt() + { + this.mapIteInt = new Function(Token.NoToken, "linear_" + domainName + "_MapIteInt", + new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeBool), true), + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeInt), true), + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeInt), true) }, + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "d", mapTypeInt), false)); + if (CommandLineOptions.Clo.UseArrayTheory) + { + this.mapIteInt.AddAttribute("builtin", "MapIte"); + } + else + { + AxiomVariable a = new AxiomVariable("a", mapTypeBool); + AxiomVariable b = new AxiomVariable("b", mapTypeInt); + AxiomVariable c = new AxiomVariable("c", mapTypeInt); + AxiomVariable x = new AxiomVariable("x", elementType); + var mapApplTerm = ExprHelper.FunctionCall(mapIteInt, a.Ident, b.Ident, c.Ident); + var lhsTerm = Expr.Select(mapApplTerm, x.Ident); + var rhsTerm = ExprHelper.IfThenElse(Expr.Select(a.Ident, x.Ident), Expr.Select(b.Ident, x.Ident), Expr.Select(c.Ident, x.Ident)); + var axiomExpr = new ForallExpr(Token.NoToken, new List(), new List { a.Bound, b.Bound, c.Bound }, null, + new Trigger(Token.NoToken, true, new List { mapApplTerm }), + new ForallExpr(Token.NoToken, new List { x.Bound }, Expr.Eq(lhsTerm, rhsTerm))); + axiomExpr.Typecheck(new TypecheckingContext(null)); + axioms.Add(new Axiom(Token.NoToken, axiomExpr)); + } + } + + private void MapLeInt() + { + this.mapLeInt = new Function(Token.NoToken, "linear_" + domainName + "_MapLe", + new List { new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "a", mapTypeInt), true), + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "b", mapTypeInt), true) }, + new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "c", mapTypeBool), false)); + if (CommandLineOptions.Clo.UseArrayTheory) + { + this.mapLeInt.AddAttribute("builtin", "MapLe"); + } + else + { + AxiomVariable a = new AxiomVariable("a", mapTypeInt); + AxiomVariable b = new AxiomVariable("b", mapTypeInt); + AxiomVariable x = new AxiomVariable("x", elementType); + var mapApplTerm = ExprHelper.FunctionCall(mapLeInt, a.Ident, b.Ident); + var lhsTerm = Expr.Select(mapApplTerm, x.Ident); + var rhsTerm = Expr.Le(Expr.Select(a.Ident, x.Ident), Expr.Select(b.Ident, x.Ident)); + var axiomExpr = new ForallExpr(Token.NoToken, new List(), new List { a.Bound, b.Bound }, null, + new Trigger(Token.NoToken, true, new List { mapApplTerm }), + new ForallExpr(Token.NoToken, new List { x.Bound }, Expr.Eq(lhsTerm, rhsTerm))); + axiomExpr.Typecheck(new TypecheckingContext(null)); + axioms.Add(new Axiom(Token.NoToken, axiomExpr)); + } + } + } + + /// + /// Type checker for linear type annotations. + /// + /// The functionality is basically grouped into four parts (see #region's). + /// 1) TypeCheck parses linear type attributes, sets up the data structures, + /// and performs a dataflow check on procedure implementations. + /// 2) Program transformation(s) to inject logical disjointness annotations. + /// 3) Generation of linearity-invariant checker procedures for atomic actions. + /// 4) Erasure procedure to remove all linearity attributes + /// (invoked after all other CIVL transformations). + /// + public class LinearTypeChecker : ReadOnlyVisitor + { + public Program program; + public CheckingContext checkingContext; + public Dictionary linearDomains; + public Dictionary domainNameToHoleVar; + + private CivlTypeChecker ctc; + + private Dictionary> availableLinearVars; + private Dictionary inParamToLinearQualifier; + private Dictionary outParamToDomainName; + private Dictionary globalVarToDomainName; + + // Only used in visitor implementation + private Dictionary> domainNameToCollectors; + private Dictionary varToDomainName; + + public LinearTypeChecker(Program program, CivlTypeChecker ctc) + { + this.program = program; + this.ctc = ctc; + this.checkingContext = new CheckingContext(null); + this.domainNameToCollectors = new Dictionary>(); + this.availableLinearVars = new Dictionary>(); + this.inParamToLinearQualifier = new Dictionary(); + this.outParamToDomainName = new Dictionary(); + this.globalVarToDomainName = new Dictionary(); + this.linearDomains = new Dictionary(); + this.domainNameToHoleVar = new Dictionary(); + this.varToDomainName = new Dictionary(); + } + + private void Error(Absy node, string message) + { + checkingContext.Error(node, message); + } + + public string FindDomainName(Variable v) + { + if (globalVarToDomainName.ContainsKey(v)) + return globalVarToDomainName[v]; + if (inParamToLinearQualifier.ContainsKey(v)) + return inParamToLinearQualifier[v].domainName; + if (outParamToDomainName.ContainsKey(v)) + return outParamToDomainName[v]; + string domainName = QKeyValue.FindStringAttribute(v.Attributes, CivlAttributes.LINEAR); + if (domainName != null) + return domainName; + domainName = QKeyValue.FindStringAttribute(v.Attributes, CivlAttributes.LINEAR_IN); + if (domainName != null) + return domainName; + return QKeyValue.FindStringAttribute(v.Attributes, CivlAttributes.LINEAR_OUT); + } + + public LinearKind FindLinearKind(Variable v) + { + if (globalVarToDomainName.ContainsKey(v)) + return LinearKind.LINEAR; + if (inParamToLinearQualifier.ContainsKey(v)) + return inParamToLinearQualifier[v].kind; + if (outParamToDomainName.ContainsKey(v)) + return LinearKind.LINEAR; + + if (QKeyValue.FindStringAttribute(v.Attributes, CivlAttributes.LINEAR) != null) + { + return LinearKind.LINEAR; + } + else if (QKeyValue.FindStringAttribute(v.Attributes, CivlAttributes.LINEAR_IN) != null) + { + return LinearKind.LINEAR_IN; + } + else if (QKeyValue.FindStringAttribute(v.Attributes, CivlAttributes.LINEAR_OUT) != null) + { + return LinearKind.LINEAR_OUT; + } + else + { + return LinearKind.ORDINARY; + } + } + + public Formal LinearDomainInFormal(string domainName) + { return new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_in", linearDomains[domainName].mapTypeBool), true); } + + public LocalVariable LinearDomainAvailableLocal(string domainName) + { return new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_available", linearDomains[domainName].mapTypeBool)); } + + private GlobalVariable LinearDomainHoleGlobal(string domainName) + { return new GlobalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "linear_" + domainName + "_hole", linearDomains[domainName].mapTypeBool)); } + + public void TypeCheck() + { + this.VisitProgram(program); + foreach (var variable in varToDomainName.Keys) + { + string domainName = FindDomainName(variable); + if (!domainNameToCollectors.ContainsKey(domainName) || + !domainNameToCollectors[domainName].ContainsKey(variable.TypedIdent.Type)) + { + Error(variable, "Missing collector for linear variable " + variable.Name); + } + } + foreach (string domainName in domainNameToCollectors.Keys) + { + var collectors = domainNameToCollectors[domainName]; + if (collectors.Count == 0) continue; + this.linearDomains[domainName] = new LinearDomain(program, domainName, collectors); + } + foreach (Absy absy in this.availableLinearVars.Keys) + { + availableLinearVars[absy].RemoveWhere(v => v is GlobalVariable); + } + } + + #region Visitor Implementation + public override Program VisitProgram(Program node) + { + foreach (GlobalVariable g in program.GlobalVariables) + { + string domainName = FindDomainName(g); + if (domainName != null) + { + globalVarToDomainName[g] = domainName; + } + } + return base.VisitProgram(node); + } + + public override Function VisitFunction(Function node) + { + string domainName = QKeyValue.FindStringAttribute(node.Attributes, CivlAttributes.LINEAR); + if (domainName != null) + { + if (!domainNameToCollectors.ContainsKey(domainName)) + { + domainNameToCollectors[domainName] = new Dictionary(); + } + if (node.InParams.Count == 1 && node.OutParams.Count == 1) + { + Type inType = node.InParams[0].TypedIdent.Type; + MapType outType = node.OutParams[0].TypedIdent.Type as MapType; + if (domainNameToCollectors[domainName].ContainsKey(inType)) + { + Error(node, "A collector for domain for input type has already been defined"); + } + else if (outType == null || outType.Arguments.Count != 1 || !outType.Result.Equals(Type.Bool)) + { + Error(node, "Output of a linear domain collector should be of set type"); + } + else + { + domainNameToCollectors[domainName][inType] = node; + } + } + else + { + Error(node, "Linear domain collector should have one input and one output parameter"); + } + } + return base.VisitFunction(node); + } + public override Implementation VisitImplementation(Implementation node) + { + if (ctc.procToAtomicAction.ContainsKey(node.Proc)) + return node; + if (ctc.procToAtomicAction.Values.SelectMany(a => a.layerToActionCopy.Values).Select(a => a.impl).Contains(node)) + return node; + + node.PruneUnreachableBlocks(); + node.ComputePredecessorsForBlocks(); + GraphUtil.Graph graph = Program.GraphFromImpl(node); + graph.ComputeLoops(); + + HashSet start = new HashSet(globalVarToDomainName.Keys); + for (int i = 0; i < node.InParams.Count; i++) + { + Variable v = node.Proc.InParams[i]; + string domainName = FindDomainName(v); + if (domainName != null) + { + var kind = FindLinearKind(v); + inParamToLinearQualifier[node.InParams[i]] = new LinearQualifier(domainName, kind); + if (kind == LinearKind.LINEAR || kind == LinearKind.LINEAR_IN) + { + start.Add(node.InParams[i]); + } + } + } + for (int i = 0; i < node.OutParams.Count; i++) + { + string domainName = FindDomainName(node.Proc.OutParams[i]); + if (domainName != null) + { + outParamToDomainName[node.OutParams[i]] = domainName; + } + } + + var oldErrorCount = checkingContext.ErrorCount; + var impl = base.VisitImplementation(node); + if (oldErrorCount < checkingContext.ErrorCount) + return impl; + + Stack dfsStack = new Stack(); + HashSet dfsStackAsSet = new HashSet(); + availableLinearVars[node.Blocks[0]] = start; + dfsStack.Push(node.Blocks[0]); + dfsStackAsSet.Add(node.Blocks[0]); + while (dfsStack.Count > 0) + { + Block b = dfsStack.Pop(); + dfsStackAsSet.Remove(b); + HashSet end = PropagateAvailableLinearVarsAcrossBlock(b); + if (b.TransferCmd is ReturnCmd) + { + foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(end)) + { + Error(b.TransferCmd, $"Global variable {g.Name} must be available at a return"); + } + foreach (Variable v in node.InParams) + { + if (FindDomainName(v) == null || FindLinearKind(v) == LinearKind.LINEAR_IN || end.Contains(v)) continue; + Error(b.TransferCmd, $"Input variable {v.Name} must be available at a return"); + } + foreach (Variable v in node.OutParams) + { + if (FindDomainName(v) == null || end.Contains(v)) continue; + Error(b.TransferCmd, $"Output variable {v.Name} must be available at a return"); + } + continue; + } + GotoCmd gotoCmd = b.TransferCmd as GotoCmd; + foreach (Block target in gotoCmd.labelTargets) + { + if (!availableLinearVars.ContainsKey(target)) + { + availableLinearVars[target] = new HashSet(end); + dfsStack.Push(target); + dfsStackAsSet.Add(target); + } + else + { + var savedAvailableVars = new HashSet(availableLinearVars[target]); + availableLinearVars[target].IntersectWith(end); + if (savedAvailableVars.IsProperSupersetOf(availableLinearVars[target]) && !dfsStackAsSet.Contains(target)) + { + dfsStack.Push(target); + dfsStackAsSet.Add(target); + } + } + } + } + + if (graph.Reducible) + { + foreach (Block header in graph.Headers) + { + foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(availableLinearVars[header])) + { + Error(header, $"Global variable {g.Name} must be available at a loop head"); + } + } + } + return impl; + } + private void AddAvailableVars(CallCmd callCmd, HashSet start) + { + foreach (IdentifierExpr ie in callCmd.Outs) + { + if (FindDomainName(ie.Decl) == null) continue; + start.Add(ie.Decl); + } + for (int i = 0; i < callCmd.Proc.InParams.Count; i++) + { + if (callCmd.Ins[i] is IdentifierExpr ie) + { + Variable v = callCmd.Proc.InParams[i]; + if (FindDomainName(v) == null) continue; + if (FindLinearKind(v) == LinearKind.LINEAR_OUT) + { + start.Add(ie.Decl); + } + } + } + } + private void AddAvailableVars(ParCallCmd parCallCmd, HashSet start) + { + foreach (CallCmd callCmd in parCallCmd.CallCmds) + { + AddAvailableVars(callCmd, start); + } + } + private HashSet PropagateAvailableLinearVarsAcrossBlock(Block b) + { + HashSet start = new HashSet(availableLinearVars[b]); + foreach (Cmd cmd in b.Cmds) + { + if (cmd is AssignCmd assignCmd) + { + for (int i = 0; i < assignCmd.Lhss.Count; i++) + { + if (FindDomainName(assignCmd.Lhss[i].DeepAssignedVariable) == null) continue; + IdentifierExpr ie = assignCmd.Rhss[i] as IdentifierExpr; + if (!start.Contains(ie.Decl)) + { + Error(ie, "unavailable source for a linear read"); + } + else + { + start.Remove(ie.Decl); + } + } + foreach (AssignLhs assignLhs in assignCmd.Lhss) + { + if (FindDomainName(assignLhs.DeepAssignedVariable) == null) continue; + start.Add(assignLhs.DeepAssignedVariable); + } + } + else if (cmd is CallCmd callCmd) + { + foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(start)) + { + Error(cmd, $"Global variable {g.Name} must be available at a call"); + } + for (int i = 0; i < callCmd.Proc.InParams.Count; i++) + { + Variable param = callCmd.Proc.InParams[i]; + if (FindDomainName(param) == null) continue; + IdentifierExpr ie = callCmd.Ins[i] as IdentifierExpr; + LinearKind paramKind = FindLinearKind(param); + if (start.Contains(ie.Decl)) + { + if (callCmd.IsAsync || paramKind == LinearKind.LINEAR_IN) + { + start.Remove(ie.Decl); + } + } + else + { + if (paramKind == LinearKind.LINEAR_OUT) + { + start.Add(ie.Decl); + } + else + { + Error(ie, "unavailable source for a linear read"); + } + } + } + AddAvailableVars(callCmd, start); + availableLinearVars[callCmd] = new HashSet(start); + } + else if (cmd is ParCallCmd parCallCmd) + { + foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(start)) + { + Error(cmd, $"Global variable {g.Name} must be available at a call"); + } + foreach (CallCmd parCallCallCmd in parCallCmd.CallCmds) + { + for (int i = 0; i < parCallCallCmd.Proc.InParams.Count; i++) + { + Variable param = parCallCallCmd.Proc.InParams[i]; + if (FindDomainName(param) == null) continue; + IdentifierExpr ie = parCallCallCmd.Ins[i] as IdentifierExpr; + LinearKind paramKind = FindLinearKind(param); + if (start.Contains(ie.Decl)) + { + if (paramKind == LinearKind.LINEAR_IN) + { + start.Remove(ie.Decl); + } + } + else + { + if (paramKind == LinearKind.LINEAR_OUT) + { + start.Add(ie.Decl); + } + else + { + Error(ie, "unavailable source for a linear read"); + } + } + } + } + AddAvailableVars(parCallCmd, start); + availableLinearVars[parCallCmd] = new HashSet(start); + } + else if (cmd is HavocCmd havocCmd) + { + foreach (IdentifierExpr ie in havocCmd.Vars) + { + if (FindDomainName(ie.Decl) == null) continue; + start.Remove(ie.Decl); + } + } + else if (cmd is YieldCmd) + { + foreach (GlobalVariable g in globalVarToDomainName.Keys.Except(start)) + { + Error(cmd, $"Global variable {g.Name} must be available at a yield"); + } + availableLinearVars[cmd] = new HashSet(start); + } + } + return start; + } + public override Variable VisitVariable(Variable node) + { + string domainName = FindDomainName(node); + if (domainName != null) + { + varToDomainName[node] = domainName; + LinearKind kind = FindLinearKind(node); + if (kind != LinearKind.LINEAR) + { + if (node is GlobalVariable || node is LocalVariable || (node is Formal formal && !formal.InComing)) + { + Error(node, "Variable must be declared linear (as opposed to linear_in or linear_out)"); + } + } + } + return base.VisitVariable(node); + } + public override Cmd VisitAssignCmd(AssignCmd node) + { + HashSet rhsVars = new HashSet(); + for (int i = 0; i < node.Lhss.Count; i++) + { + AssignLhs lhs = node.Lhss[i]; + Variable lhsVar = lhs.DeepAssignedVariable; + string domainName = FindDomainName(lhsVar); + if (domainName == null) continue; + if (!(lhs is SimpleAssignLhs)) + { + Error(node, $"Only simple assignment allowed on linear variable {lhsVar.Name}"); + continue; + } + IdentifierExpr rhs = node.Rhss[i] as IdentifierExpr; + if (rhs == null) + { + Error(node, $"Only variable can be assigned to linear variable {lhsVar.Name}"); + continue; + } + string rhsDomainName = FindDomainName(rhs.Decl); + if (rhsDomainName == null) + { + Error(node, $"Only linear variable can be assigned to linear variable {lhsVar.Name}"); + continue; + } + if (domainName != rhsDomainName) + { + Error(node, $"Linear variable of domain {rhsDomainName} cannot be assigned to linear variable of domain {domainName}"); + continue; + } + if (rhsVars.Contains(rhs.Decl)) + { + Error(node, $"Linear variable {rhs.Decl.Name} can occur only once in the right-hand-side of an assignment"); + continue; + } + rhsVars.Add(rhs.Decl); + } + return base.VisitAssignCmd(node); + } + public override Cmd VisitCallCmd(CallCmd node) + { + HashSet inVars = new HashSet(); + for (int i = 0; i < node.Proc.InParams.Count; i++) + { + Variable formal = node.Proc.InParams[i]; + string domainName = FindDomainName(formal); + if (domainName == null) continue; + IdentifierExpr actual = node.Ins[i] as IdentifierExpr; + if (actual == null) + { + Error(node, $"Only variable can be passed to linear parameter {formal.Name}"); + continue; + } + string actualDomainName = FindDomainName(actual.Decl); + if (actualDomainName == null) + { + Error(node, $"Only a linear argument can be passed to linear parameter {formal.Name}"); + continue; + } + if (domainName != actualDomainName) + { + Error(node, "The domains of formal and actual parameters must be the same"); + continue; + } + if (actual.Decl is GlobalVariable) + { + Error(node, "Only local linear variable can be an actual input parameter of a procedure call"); + continue; + } + if (inVars.Contains(actual.Decl)) + { + Error(node, $"Linear variable {actual.Decl.Name} can occur only once as an input parameter"); + continue; + } + inVars.Add(actual.Decl); + } + for (int i = 0; i < node.Proc.OutParams.Count; i++) + { + IdentifierExpr actual = node.Outs[i]; + string actualDomainName = FindDomainName(actual.Decl); + if (actualDomainName == null) continue; + Variable formal = node.Proc.OutParams[i]; + string domainName = FindDomainName(formal); + if (domainName == null) + { + Error(node, "Only a linear variable can be passed to a linear parameter"); + continue; + } + if (domainName != actualDomainName) + { + Error(node, "The domains of formal and actual parameters must be the same"); + continue; + } + if (actual.Decl is GlobalVariable) + { + Error(node, "Only local linear variable can be actual output parameter of a procedure call"); + continue; + } + } + return base.VisitCallCmd(node); + } + public override Cmd VisitParCallCmd(ParCallCmd node) + { + HashSet parallelCallInvars = new HashSet(); + foreach (CallCmd callCmd in node.CallCmds) + { + for (int i = 0; i < callCmd.Proc.InParams.Count; i++) + { + Variable formal = callCmd.Proc.InParams[i]; + string domainName = FindDomainName(formal); + if (domainName == null) continue; + IdentifierExpr actual = callCmd.Ins[i] as IdentifierExpr; + if (parallelCallInvars.Contains(actual.Decl)) + { + Error(node, $"Linear variable {actual.Decl.Name} can occur only once as an input parameter of a parallel call"); + } + else + { + parallelCallInvars.Add(actual.Decl); + } + } + } + return base.VisitParCallCmd(node); + } + + public override Requires VisitRequires(Requires requires) + { + return requires; + } + + public override Ensures VisitEnsures(Ensures ensures) + { + return ensures; + } + #endregion + + #region Program Transformation + public void Transform() + { + // The disjointness expressions injected below include an additional placeholder variable (a "hole") + // which is substituted in yield checker implementations. + foreach (string domainName in linearDomains.Keys) + { + GlobalVariable g = LinearDomainHoleGlobal(domainName); + program.AddTopLevelDeclaration(g); + domainNameToHoleVar[domainName] = g; + } + + foreach (var impl in program.Implementations) + { + foreach (Block b in impl.Blocks) + { + List newCmds = new List(); + for (int i = 0; i < b.Cmds.Count; i++) + { + Cmd cmd = b.Cmds[i]; + newCmds.Add(cmd); + if (cmd is CallCmd || cmd is ParCallCmd || cmd is YieldCmd) + { + AddDisjointnessExpr(newCmds, cmd); + } + } + b.Cmds = newCmds; + } + + { + // Loops + impl.PruneUnreachableBlocks(); + impl.ComputePredecessorsForBlocks(); + GraphUtil.Graph g = Program.GraphFromImpl(impl); + g.ComputeLoops(); + if (g.Reducible) + { + foreach (Block header in g.Headers) + { + List newCmds = new List(); + AddDisjointnessExpr(newCmds, header); + newCmds.AddRange(header.Cmds); + header.Cmds = newCmds; + } + } + } + } + + foreach (var proc in program.Procedures) + { + Dictionary> domainNameToInputScope = new Dictionary>(); + Dictionary> domainNameToOutputScope = new Dictionary>(); + foreach (var domainName in linearDomains.Keys) + { + domainNameToInputScope[domainName] = new HashSet(); + domainNameToOutputScope[domainName] = new HashSet(); + } + foreach (Variable v in globalVarToDomainName.Keys) + { + var domainName = globalVarToDomainName[v]; + domainNameToInputScope[domainName].Add(v); + domainNameToOutputScope[domainName].Add(v); + } + foreach (Variable v in proc.InParams) + { + var domainName = FindDomainName(v); + if (domainName == null) continue; + if (!this.linearDomains.ContainsKey(domainName)) continue; + domainNameToInputScope[domainName].Add(v); + } + foreach (Variable v in proc.OutParams) + { + var domainName = FindDomainName(v); + if (domainName == null) continue; + if (!this.linearDomains.ContainsKey(domainName)) continue; + domainNameToOutputScope[domainName].Add(v); + } + // TODO: Also add linear and linear_out parameters to domainNameToOutputScope? + // This should still be sound and strengthen the generated postcondition. + foreach (var domainName in linearDomains.Keys) + { + Expr req = DisjointnessExpr(domainName, domainNameToInputScope[domainName]); + Expr ens = DisjointnessExpr(domainName, domainNameToOutputScope[domainName]); + if (!req.Equals(Expr.True)) + proc.Requires.Add(new Requires(true, req)); + if (!ens.Equals(Expr.True)) + proc.Ensures.Add(new Ensures(true, ens)); + } + } + + foreach (LinearDomain domain in linearDomains.Values) + { + program.AddTopLevelDeclaration(domain.mapConstBool); + program.AddTopLevelDeclaration(domain.mapConstInt); + program.AddTopLevelDeclaration(domain.mapEqInt); + program.AddTopLevelDeclaration(domain.mapImpBool); + program.AddTopLevelDeclaration(domain.mapOrBool); + program.AddTopLevelDeclaration(domain.mapAddInt); + program.AddTopLevelDeclaration(domain.mapLeInt); + program.AddTopLevelDeclaration(domain.mapIteInt); + foreach (Axiom axiom in domain.axioms) + { + program.AddTopLevelDeclaration(axiom); + } + } + + //int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured; + //CommandLineOptions.Clo.PrintUnstructured = 1; + //PrintBplFile("lsd.bpl", program, false, false); + //CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + } + + public IEnumerable AvailableLinearVars(Absy absy) + { + if (availableLinearVars.ContainsKey(absy)) + { + return availableLinearVars[absy]; + } + else + { + return new HashSet(); + } + } + + private void AddDisjointnessExpr(List newCmds, Absy absy) + { + Dictionary> domainNameToScope = new Dictionary>(); + foreach (var domainName in linearDomains.Keys) + { + domainNameToScope[domainName] = new HashSet(); + } + foreach (Variable v in AvailableLinearVars(absy)) + { + var domainName = FindDomainName(v); + domainNameToScope[domainName].Add(v); + } + foreach (Variable v in globalVarToDomainName.Keys) + { + var domainName = FindDomainName(v); + domainNameToScope[domainName].Add(v); + } + foreach (string domainName in linearDomains.Keys) + { + Expr expr = DisjointnessExpr(domainName, domainNameToHoleVar[domainName], domainNameToScope[domainName]); + if (!expr.Equals(Expr.True)) + newCmds.Add(new AssumeCmd(Token.NoToken, expr)); + } + } + + private Expr SubsetExpr(LinearDomain domain, Expr ie, Variable partition, int partitionCount) + { + Expr e = ExprHelper.FunctionCall(domain.mapConstInt, Expr.Literal(partitionCount)); + e = ExprHelper.FunctionCall(domain.mapEqInt, Expr.Ident(partition), e); + e = ExprHelper.FunctionCall(domain.mapImpBool, ie, e); + e = Expr.Eq(e, ExprHelper.FunctionCall(domain.mapConstBool, Expr.True)); + return e; + } + + private Expr DisjointnessExpr(string domainName, Variable holeVar, HashSet scope) + { + int count = 0; + List subsetExprs = new List(); + + if (scope.Count == 0 || (holeVar == null && scope.Count == 1)) + { + return Expr.True; + } + + LinearDomain domain = linearDomains[domainName]; + BoundVariable partition = new BoundVariable( + Token.NoToken, + new TypedIdent(Token.NoToken, + $"partition_{domainName}", + domain.mapTypeInt)); + + if (holeVar != null) + { + subsetExprs.Add(SubsetExpr(domain, Expr.Ident(holeVar), partition, count)); + count++; + } + + foreach (Variable v in scope) + { + Expr ie = ExprHelper.FunctionCall(domain.collectors[v.TypedIdent.Type], Expr.Ident(v)); + subsetExprs.Add(SubsetExpr(domain, ie, partition, count)); + count++; + } + var expr = new ExistsExpr(Token.NoToken, + new List { partition }, + Expr.And(subsetExprs)); + expr.Resolve(new ResolutionContext(null)); + expr.Typecheck(new TypecheckingContext(null)); + + return expr; + } + + public Expr DisjointnessExpr(string domainName, HashSet scope) + { + return DisjointnessExpr(domainName, null, scope); + } + #endregion + + #region Linearity Invariant Checker + public static void AddCheckers(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) + { + if (civlTypeChecker.procToAtomicAction.Count == 0) + return; + + foreach (var action in civlTypeChecker.procToAtomicAction.Values + .SelectMany(a => a.layerToActionCopy.Values)) + { + AddChecker(action, linearTypeChecker, decls); + } + } + + private static void AddChecker(AtomicActionCopy action, LinearTypeChecker linearTypeChecker, List decls) + { + // Note: The implementation should be used as the variables in the + // gate are bound to implementation and not to the procedure. + Implementation impl = action.impl; + List inputs = impl.InParams; + List outputs = impl.OutParams; + + // Linear out vars + List outVars; + { + LinearKind[] validKinds = { LinearKind.LINEAR, LinearKind.LINEAR_OUT }; + outVars = inputs. + Union(outputs). + Union(action.modifiedGlobalVars). + Where(x => validKinds.Contains(linearTypeChecker.FindLinearKind(x))) + .ToList(); + } + + if (outVars.Count == 0) return; + + // Linear in vars + List inVars; + { + LinearKind[] validKinds = { LinearKind.LINEAR, LinearKind.LINEAR_IN }; + inVars = inputs. + Union(action.modifiedGlobalVars). + Where(x => validKinds.Contains(linearTypeChecker.FindLinearKind(x))) + .ToList(); + } + + List requires = action.gate.Select(a => new Requires(false, a.Expr)).ToList(); + List ensures = new List(); + + // Generate linearity checks + IEnumerable domainNames = outVars. + Select(linearTypeChecker.FindDomainName).Distinct(); + foreach (var domainName in domainNames) + { + LinearDomain domain = linearTypeChecker.linearDomains[domainName]; + Expr inMultiset = linearTypeChecker.PermissionMultiset(domain, inVars, true); + Expr outMultiset = linearTypeChecker.PermissionMultiset(domain, outVars); + Expr subsetExpr = ExprHelper.FunctionCall(domain.mapLeInt, outMultiset, inMultiset); + Expr eqExpr = Expr.Eq(subsetExpr, ExprHelper.FunctionCall(domain.mapConstBool, Expr.True)); + + Ensures ensureCheck = new Ensures(action.proc.tok, false, eqExpr, null); + ensureCheck.ErrorData = $"Linearity invariant for domain {domainName} is not preserved by {action.proc.Name}."; + ResolutionContext rc = new ResolutionContext(null); + rc.StateMode = ResolutionContext.State.Two; + ensureCheck.Resolve(rc); + ensureCheck.Typecheck(new TypecheckingContext(null)); + ensures.Add(ensureCheck); + } + + // Create blocks + List blocks = new List(); + { + CallCmd cmd = new CallCmd(Token.NoToken, impl.Name, + inputs.Select(Expr.Ident).ToList(), + outputs.Select(Expr.Ident).ToList()); + cmd.Proc = action.proc; + Block block = new Block(Token.NoToken, "entry", new List { cmd }, new ReturnCmd(Token.NoToken)); + blocks.Add(block); + } + + // Create the whole check procedure + string checkerName = $"LinearityChecker_{action.proc.Name}"; + Procedure linCheckerProc = new Procedure(Token.NoToken, checkerName, new List(), + inputs, outputs, requires, action.proc.Modifies, ensures); + Implementation linCheckImpl = new Implementation(Token.NoToken, checkerName, + new List(), inputs, outputs, new List { }, blocks); + linCheckImpl.Proc = linCheckerProc; + decls.Add(linCheckImpl); + decls.Add(linCheckerProc); + } + + private Expr PermissionMultiset(LinearDomain domain, IEnumerable vars, bool useOldExpr = false) + { + Expr mapConstInt0 = ExprHelper.FunctionCall(domain.mapConstInt, Expr.Literal(0)); + Expr mapConstInt1 = ExprHelper.FunctionCall(domain.mapConstInt, Expr.Literal(1)); + + var terms = vars. + Where(x => FindDomainName(x) == domain.domainName). + Select(x => ExprHelper.FunctionCall(domain.mapIteInt, + CollectedLinearVariable(x, domain.collectors[x.TypedIdent.Type], useOldExpr), + mapConstInt1, + mapConstInt0)). + ToList(); + + if (terms.Count == 0) + return mapConstInt0; + return terms.Aggregate((x, y) => ExprHelper.FunctionCall(domain.mapAddInt, x, y)); + } + + private Expr CollectedLinearVariable(Variable v, Function collector, bool useOldExpr = false) + { + Expr arg = Expr.Ident(v); + if (useOldExpr) + arg = ExprHelper.Old(arg); + return ExprHelper.FunctionCall(collector, arg); + } + #endregion + + #region Annotation Eraser + public void EraseLinearAnnotations() + { + new LinearTypeEraser().VisitProgram(program); + } + + public class LinearTypeEraser : ReadOnlyVisitor + { + public override Variable VisitVariable(Variable node) + { + CivlAttributes.RemoveLinearAttribute(node); + return base.VisitVariable(node); + } + + public override Function VisitFunction(Function node) + { + CivlAttributes.RemoveLinearAttribute(node); + return base.VisitFunction(node); + } + } + #endregion + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/MoverCheck.cs boogie-2.4.1+dfsg/Source/Concurrency/MoverCheck.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/MoverCheck.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/MoverCheck.cs 2019-10-25 18:17:05.000000000 +0000 @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Diagnostics.Contracts; -using System.Diagnostics; namespace Microsoft.Boogie { @@ -12,662 +9,287 @@ LinearTypeChecker linearTypeChecker; CivlTypeChecker civlTypeChecker; List decls; - HashSet> commutativityCheckerCache; - HashSet> gatePreservationCheckerCache; - HashSet> failurePreservationCheckerCache; + + HashSet> commutativityCheckerCache; + HashSet> gatePreservationCheckerCache; + HashSet> failurePreservationCheckerCache; + private MoverCheck(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) { this.linearTypeChecker = linearTypeChecker; this.civlTypeChecker = civlTypeChecker; this.decls = decls; - this.commutativityCheckerCache = new HashSet>(); - this.gatePreservationCheckerCache = new HashSet>(); - this.failurePreservationCheckerCache = new HashSet>(); + this.commutativityCheckerCache = new HashSet>(); + this.gatePreservationCheckerCache = new HashSet>(); + this.failurePreservationCheckerCache = new HashSet>(); } public static void AddCheckers(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) { - if (civlTypeChecker.procToActionInfo.Count == 0) + if (civlTypeChecker.procToAtomicAction.Count == 0) return; - List sortedByCreatedLayerNum = new List(civlTypeChecker.procToActionInfo.Values.Where(x => x is AtomicActionInfo && !x.isExtern)); - sortedByCreatedLayerNum.Sort((x, y) => { return (x.createdAtLayerNum == y.createdAtLayerNum) ? 0 : (x.createdAtLayerNum < y.createdAtLayerNum) ? -1 : 1; }); - List sortedByAvailableUptoLayerNum = new List(civlTypeChecker.procToActionInfo.Values.Where(x => x is AtomicActionInfo && !x.isExtern)); - sortedByAvailableUptoLayerNum.Sort((x, y) => { return (x.availableUptoLayerNum == y.availableUptoLayerNum) ? 0 : (x.availableUptoLayerNum < y.availableUptoLayerNum) ? -1 : 1; }); - - Dictionary> pools = new Dictionary>(); - int indexIntoSortedByCreatedLayerNum = 0; - int indexIntoSortedByAvailableUptoLayerNum = 0; - HashSet currPool = new HashSet(); - while (indexIntoSortedByCreatedLayerNum < sortedByCreatedLayerNum.Count) - { - var currLayerNum = sortedByCreatedLayerNum[indexIntoSortedByCreatedLayerNum].createdAtLayerNum; - pools[currLayerNum] = new HashSet(currPool); - while (indexIntoSortedByCreatedLayerNum < sortedByCreatedLayerNum.Count) - { - var actionInfo = sortedByCreatedLayerNum[indexIntoSortedByCreatedLayerNum] as AtomicActionInfo; - if (actionInfo.createdAtLayerNum > currLayerNum) break; - pools[currLayerNum].Add(actionInfo); - indexIntoSortedByCreatedLayerNum++; - } - while (indexIntoSortedByAvailableUptoLayerNum < sortedByAvailableUptoLayerNum.Count) - { - var actionInfo = sortedByAvailableUptoLayerNum[indexIntoSortedByAvailableUptoLayerNum] as AtomicActionInfo; - if (actionInfo.availableUptoLayerNum > currLayerNum) break; - pools[currLayerNum].Remove(actionInfo); - indexIntoSortedByAvailableUptoLayerNum++; - } - currPool = pools[currLayerNum]; - } - - Program program = civlTypeChecker.program; MoverCheck moverChecking = new MoverCheck(linearTypeChecker, civlTypeChecker, decls); - foreach (int layerNum in pools.Keys) - { - foreach (AtomicActionInfo first in pools[layerNum]) - { - Debug.Assert(first.moverType != MoverType.Top); - if (first.moverType == MoverType.Atomic) - continue; - foreach (AtomicActionInfo second in pools[layerNum]) - { - if (first.IsRightMover) - { - moverChecking.CreateCommutativityChecker(program, first, second); - moverChecking.CreateGatePreservationChecker(program, second, first); - } - if (first.IsLeftMover) - { - moverChecking.CreateCommutativityChecker(program, second, first); - moverChecking.CreateGatePreservationChecker(program, first, second); - moverChecking.CreateFailurePreservationChecker(program, second, first); - } - } - } - } - foreach (AtomicActionInfo atomicActionInfo in sortedByCreatedLayerNum) - { - if (atomicActionInfo.IsLeftMover && atomicActionInfo.hasAssumeCmd) - { - moverChecking.CreateNonBlockingChecker(program, atomicActionInfo); - } - } - } - - public sealed class MyDuplicator : Duplicator - { - public override Expr VisitIdentifierExpr(IdentifierExpr node) - { - IdentifierExpr ret = (IdentifierExpr) base.VisitIdentifierExpr(node); - if (ret.Decl is GlobalVariable) - { - return new OldExpr(Token.NoToken, ret); - } - else - { - return ret; - } - } - } - - public class TransitionRelationComputation - { - private Program program; - private AtomicActionInfo first; // corresponds to that* - private AtomicActionInfo second; // corresponds to this* - private Stack cmdStack; - private List paths; - private HashSet frame; - private HashSet postExistVars; - - public TransitionRelationComputation(Program program, AtomicActionInfo second, HashSet frame, HashSet postExistVars) - { - this.postExistVars = postExistVars; - this.frame = frame; - TransitionRelationComputationHelper(program, null, second); - } - - public TransitionRelationComputation(Program program, AtomicActionInfo first, AtomicActionInfo second, HashSet frame, HashSet postExistVars) - { - this.postExistVars = postExistVars; - this.frame = frame; - TransitionRelationComputationHelper(program, first, second); - } - - private void TransitionRelationComputationHelper(Program program, AtomicActionInfo first, AtomicActionInfo second) - { - this.program = program; - this.first = first; - this.second = second; - this.cmdStack = new Stack(); - this.paths = new List(); - List havocVars = new List(); - this.second.thisOutParams.ForEach(v => havocVars.Add(Expr.Ident(v))); - this.second.thisAction.LocVars.ForEach(v => havocVars.Add(Expr.Ident(v))); - if (havocVars.Count > 0) - { - HavocCmd havocCmd = new HavocCmd(Token.NoToken, havocVars); - cmdStack.Push(havocCmd); - } - Search(this.second.thisAction.Blocks[0], false); - } - - private void Substitute(Dictionary map, ref List pathExprs, ref Dictionary varToExpr) - { - Substitution subst = Substituter.SubstitutionFromHashtable(map); - List oldPathExprs = pathExprs; - pathExprs = new List(); - foreach (Expr pathExpr in oldPathExprs) - { - pathExprs.Add(Substituter.Apply(subst, pathExpr)); - } - Dictionary oldVarToExpr = varToExpr; - varToExpr = new Dictionary(); - foreach (Variable v in oldVarToExpr.Keys) - { - varToExpr[v] = Substituter.Apply(subst, oldVarToExpr[v]); - } - } - - struct PathInfo - { - public HashSet existsVars; - public Dictionary varToExpr; - public List pathExprs; - - public PathInfo(HashSet existsVars, Dictionary varToExpr, List pathExprs) - { - this.existsVars = existsVars; - this.varToExpr = varToExpr; - this.pathExprs = pathExprs; - } - } - - private void FlattenAnd(Expr x, List xs) - { - NAryExpr naryExpr = x as NAryExpr; - if (naryExpr != null && naryExpr.Fun.FunctionName == "&&") - { - FlattenAnd(naryExpr.Args[0], xs); - FlattenAnd(naryExpr.Args[1], xs); - } - else - { - xs.Add(x); - } - } - - private void AddPath() - { - HashSet existsVars = new HashSet(); - Dictionary varToExpr = new Dictionary(); - foreach (Variable v in frame) - { - varToExpr[v] = Expr.Ident(v); - } - if (first != null) - { - foreach (Variable v in first.thatOutParams) - { - varToExpr[v] = Expr.Ident(v); - } - } - foreach (Variable v in second.thisOutParams) - { - varToExpr[v] = Expr.Ident(v); - } - List pathExprs = new List(); - int boundVariableCount = 0; - foreach (Cmd cmd in cmdStack) - { - if (cmd is AssumeCmd) - { - AssumeCmd assumeCmd = cmd as AssumeCmd; - FlattenAnd(assumeCmd.Expr, pathExprs); - } - else if (cmd is AssignCmd) - { - AssignCmd assignCmd = (cmd as AssignCmd).AsSimpleAssignCmd; - Dictionary map = new Dictionary(); - for (int k = 0; k < assignCmd.Lhss.Count; k++) - { - map[assignCmd.Lhss[k].DeepAssignedVariable] = assignCmd.Rhss[k]; - } - Substitute(map, ref pathExprs, ref varToExpr); - } - else if (cmd is HavocCmd) - { - HavocCmd havocCmd = cmd as HavocCmd; - Dictionary map = new Dictionary(); - foreach (IdentifierExpr ie in havocCmd.Vars) - { - BoundVariable bv = new BoundVariable(Token.NoToken, new TypedIdent(Token.NoToken, "#tmp_" + boundVariableCount++, ie.Decl.TypedIdent.Type)); - map[ie.Decl] = Expr.Ident(bv); - existsVars.Add(bv); - } - Substitute(map, ref pathExprs, ref varToExpr); - } - else - { - Debug.Assert(false); - } - } - paths.Add(new PathInfo(existsVars, varToExpr, pathExprs)); - } - private Expr CalculatePathCondition(PathInfo path) + foreach (int layer in civlTypeChecker.allAtomicActionLayers) { - Expr returnExpr = Expr.True; + var pool = civlTypeChecker.procToAtomicAction.Values.Where(a => a.layerRange.Contains(layer)); - HashSet existsVars = path.existsVars; - Dictionary existsMap = new Dictionary(); + var moverChecks = + from first in pool + from second in pool + where first.moverType != MoverType.Atomic + select new { First = first, Second = second }; - Dictionary varToExpr = path.varToExpr; - foreach (Variable v in varToExpr.Keys) + foreach (var moverCheck in moverChecks) { - if (postExistVars.Contains(v)) continue; - IdentifierExpr ie = varToExpr[v] as IdentifierExpr; - if (ie != null && !existsMap.ContainsKey(ie.Decl) && existsVars.Contains(ie.Decl)) - { - existsMap[ie.Decl] = Expr.Ident(v); - existsVars.Remove(ie.Decl); - } - else - { - returnExpr = Expr.And(returnExpr, Expr.Eq(Expr.Ident(v), (new MyDuplicator()).VisitExpr(varToExpr[v]))); - returnExpr.Type = Type.Bool; - } - } + var first = moverCheck.First.layerToActionCopy[layer]; + var second = moverCheck.Second.layerToActionCopy[layer]; - List pathExprs = new List(); - path.pathExprs.ForEach(x => pathExprs.Add((new MyDuplicator()).VisitExpr(x))); - foreach (Expr x in pathExprs) - { - Variable boundVar; - Expr boundVarExpr; - if (InferSubstitution(x, out boundVar, out boundVarExpr) && existsVars.Contains(boundVar)) + if (moverCheck.First.IsRightMover) { - existsMap[boundVar] = boundVarExpr; - existsVars.Remove(boundVar); + moverChecking.CreateCommutativityChecker(first, second); + moverChecking.CreateGatePreservationChecker(second, first); } - else + if (moverCheck.First.IsLeftMover) { - returnExpr = Expr.And(returnExpr, x); - returnExpr.Type = Type.Bool; + moverChecking.CreateCommutativityChecker(second, first); + moverChecking.CreateGatePreservationChecker(first, second); + moverChecking.CreateFailurePreservationChecker(second, first); } } - - returnExpr = Substituter.Apply(Substituter.SubstitutionFromHashtable(existsMap), returnExpr); - if (existsVars.Count > 0) - { - returnExpr = new ExistsExpr(Token.NoToken, new List(existsVars), returnExpr); - } - return returnExpr; - } - - bool InferSubstitution(Expr x, out Variable var, out Expr expr) - { - var = null; - expr = null; - NAryExpr naryExpr = x as NAryExpr; - if (naryExpr == null || naryExpr.Fun.FunctionName != "==") - { - return false; - } - IdentifierExpr arg0 = naryExpr.Args[0] as IdentifierExpr; - if (arg0 != null && arg0.Decl is BoundVariable) - { - var = arg0.Decl; - expr = naryExpr.Args[1]; - return true; - } - IdentifierExpr arg1 = naryExpr.Args[1] as IdentifierExpr; - if (arg1 != null && arg1.Decl is BoundVariable) + foreach (AtomicAction atomicAction in pool.Where(a => a.IsLeftMover)) { - var = arg1.Decl; - expr = naryExpr.Args[0]; - return true; + moverChecking.CreateNonBlockingChecker(atomicAction.layerToActionCopy[layer]); } - return false; } - - public Expr TransitionRelationCompute(bool withOriginalInOutVariables = false) - { - Expr transitionRelation = Expr.False; - foreach (PathInfo path in paths) - { - transitionRelation = Expr.Or(transitionRelation, CalculatePathCondition(path)); - } - ResolutionContext rc = new ResolutionContext(null); - rc.StateMode = ResolutionContext.State.Two; - transitionRelation.Resolve(rc); - transitionRelation.Typecheck(new TypecheckingContext(null)); - - if (withOriginalInOutVariables) - { - Dictionary invertedMap = new Dictionary(); - if (first != null) - { - foreach (var x in first.thatMap) - { - invertedMap[((IdentifierExpr)x.Value).Decl] = Expr.Ident(x.Key); - } - } - if (second != null) - { - foreach (var x in second.thisMap) - { - invertedMap[((IdentifierExpr)x.Value).Decl] = Expr.Ident(x.Key); - } - } - Substitution subst = Substituter.SubstitutionFromHashtable(invertedMap); - return Substituter.Apply(subst, transitionRelation); - } - else - { - return transitionRelation; - } - - } - - private void Search(Block b, bool inFirst) - { - int pathSizeAtEntry = cmdStack.Count; - foreach (Cmd cmd in b.Cmds) - { - cmdStack.Push(cmd); - } - if (b.TransferCmd is ReturnCmd) - { - if (first == null || inFirst) - { - AddPath(); - } - else - { - List havocVars = new List(); - first.thatOutParams.ForEach(v => havocVars.Add(Expr.Ident(v))); - first.thatAction.LocVars.ForEach(v => havocVars.Add(Expr.Ident(v))); - if (havocVars.Count > 0) - { - HavocCmd havocCmd = new HavocCmd(Token.NoToken, havocVars); - cmdStack.Push(havocCmd); - } - Search(first.thatAction.Blocks[0], true); - } - } - else - { - GotoCmd gotoCmd = b.TransferCmd as GotoCmd; - foreach (Block target in gotoCmd.labelTargets) - { - Search(target, inFirst); - } - } - Debug.Assert(cmdStack.Count >= pathSizeAtEntry); - while (cmdStack.Count > pathSizeAtEntry) - { - cmdStack.Pop(); - } - } - } - - private static List CloneBlocks(List blocks) - { - Dictionary blockMap = new Dictionary(); - List otherBlocks = new List(); - foreach (Block block in blocks) - { - List otherCmds = new List(); - foreach (Cmd cmd in block.Cmds) - { - otherCmds.Add(cmd); - } - Block otherBlock = new Block(); - otherBlock.Cmds = otherCmds; - otherBlock.Label = block.Label; - otherBlocks.Add(otherBlock); - blockMap[block] = otherBlock; - } - foreach (Block block in blocks) - { - if (block.TransferCmd is ReturnCmd) continue; - List otherGotoCmdLabelTargets = new List(); - List otherGotoCmdLabelNames = new List(); - GotoCmd gotoCmd = block.TransferCmd as GotoCmd; - foreach (Block target in gotoCmd.labelTargets) - { - otherGotoCmdLabelTargets.Add(blockMap[target]); - otherGotoCmdLabelNames.Add(blockMap[target].Label); - } - blockMap[block].TransferCmd = new GotoCmd(block.TransferCmd.tok, otherGotoCmdLabelNames, otherGotoCmdLabelTargets); - } - return otherBlocks; } - private List DisjointnessRequires(Program program, AtomicActionInfo first, AtomicActionInfo second, HashSet frame) + private IEnumerable DisjointnessExpr(IEnumerable paramVars, HashSet frame) { - List requires = new List(); Dictionary> domainNameToScope = new Dictionary>(); foreach (var domainName in linearTypeChecker.linearDomains.Keys) { domainNameToScope[domainName] = new HashSet(); } - foreach (Variable v in frame) - { - var domainName = linearTypeChecker.FindDomainName(v); - if (domainName == null) continue; - if (!linearTypeChecker.linearDomains.ContainsKey(domainName)) continue; - domainNameToScope[domainName].Add(v); - } - if (first != null) - { - foreach (Variable v in first.thatInParams) - { - var domainName = linearTypeChecker.FindDomainName(v); - if (domainName == null) continue; - if (!linearTypeChecker.linearDomains.ContainsKey(domainName)) continue; - domainNameToScope[domainName].Add(v); - } - } - foreach (Variable v in second.thisInParams) + foreach (Variable v in paramVars.Union(frame)) { var domainName = linearTypeChecker.FindDomainName(v); if (domainName == null) continue; - if (!linearTypeChecker.linearDomains.ContainsKey(domainName)) continue; domainNameToScope[domainName].Add(v); } foreach (string domainName in domainNameToScope.Keys) { - requires.Add(new Requires(false, linearTypeChecker.DisjointnessExpr(domainName, domainNameToScope[domainName]))); + yield return linearTypeChecker.DisjointnessExpr(domainName, domainNameToScope[domainName]); } - return requires; } - private void CreateCommutativityChecker(Program program, AtomicActionInfo first, AtomicActionInfo second) + private Requires DisjointnessRequires(IEnumerable paramVars, HashSet frame) + { + return new Requires(false, Expr.And(DisjointnessExpr(paramVars, frame))); + } + + private void AddChecker(string checkerName, List inputs, List outputs, List locals, List requires, List ensures, List blocks) { - if (first == second && first.thatInParams.Count == 0 && first.thatOutParams.Count == 0) + Procedure proc = new Procedure(Token.NoToken, checkerName, new List(), inputs, outputs, requires, civlTypeChecker.sharedVariableIdentifiers, ensures); + Implementation impl = new Implementation(Token.NoToken, checkerName, new List(), inputs, outputs, locals, blocks); + impl.Proc = proc; + this.decls.Add(impl); + this.decls.Add(proc); + } + + private void CreateCommutativityChecker(AtomicActionCopy first, AtomicActionCopy second) + { + if (first == second && first.firstInParams.Count == 0 && first.firstOutParams.Count == 0) return; - if (first.CommutesWith(second)) + if (first.TriviallyCommutesWith(second)) return; - Tuple actionPair = new Tuple(first, second); - if (commutativityCheckerCache.Contains(actionPair)) + if (!commutativityCheckerCache.Add(Tuple.Create(first, second))) return; - commutativityCheckerCache.Add(actionPair); - List inputs = new List(); - inputs.AddRange(first.thatInParams); - inputs.AddRange(second.thisInParams); - List outputs = new List(); - outputs.AddRange(first.thatOutParams); - outputs.AddRange(second.thisOutParams); - List locals = new List(); - locals.AddRange(first.thatAction.LocVars); - locals.AddRange(second.thisAction.LocVars); - List firstBlocks = CloneBlocks(first.thatAction.Blocks); - List secondBlocks = CloneBlocks(second.thisAction.Blocks); - foreach (Block b in firstBlocks) - { - if (b.TransferCmd is ReturnCmd) - { - List bs = new List(); - bs.Add(secondBlocks[0]); - List ls = new List(); - ls.Add(secondBlocks[0].Label); - b.TransferCmd = new GotoCmd(Token.NoToken, ls, bs); - } - } - List blocks = new List(); - blocks.AddRange(firstBlocks); - blocks.AddRange(secondBlocks); + string checkerName = $"CommutativityChecker_{first.proc.Name}_{second.proc.Name}"; + HashSet frame = new HashSet(); frame.UnionWith(first.gateUsedGlobalVars); frame.UnionWith(first.actionUsedGlobalVars); frame.UnionWith(second.gateUsedGlobalVars); frame.UnionWith(second.actionUsedGlobalVars); - List requires = DisjointnessRequires(program, first, second, frame); - foreach (AssertCmd assertCmd in first.thatGate) - requires.Add(new Requires(false, assertCmd.Expr)); - foreach (AssertCmd assertCmd in second.thisGate) + + List requires = new List { + DisjointnessRequires( + first.firstInParams. + Union(second.secondInParams). + Where(v => linearTypeChecker.FindLinearKind(v) != LinearKind.LINEAR_OUT), + frame) + }; + foreach (AssertCmd assertCmd in Enumerable.Union(first.firstGate, second.secondGate)) requires.Add(new Requires(false, assertCmd.Expr)); - List ensures = new List(); - Expr transitionRelation = (new TransitionRelationComputation(program, first, second, frame, new HashSet())).TransitionRelationCompute(); - Ensures ensureCheck = new Ensures(false, transitionRelation); - ensureCheck.ErrorData = string.Format("Commutativity check between {0} and {1} failed", first.proc.Name, second.proc.Name); - ensures.Add(ensureCheck); - string checkerName = string.Format("CommutativityChecker_{0}_{1}", first.proc.Name, second.proc.Name); - List globalVars = new List(); - civlTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); - Procedure proc = new Procedure(Token.NoToken, checkerName, new List(), inputs, outputs, requires, globalVars, ensures); - Implementation impl = new Implementation(Token.NoToken, checkerName, new List(), inputs, outputs, locals, blocks); - impl.Proc = proc; - this.decls.Add(impl); - this.decls.Add(proc); + + civlTypeChecker.atomicActionPairToWitnessFunctions.TryGetValue( + Tuple.Create(first, second), out List witnesses); + var transitionRelation = TransitionRelationComputation. + Commutativity(second, first, frame, witnesses); + + List cmds = new List + { + new CallCmd(Token.NoToken, + first.proc.Name, + first.firstInParams.Select(Expr.Ident).ToList(), + first.firstOutParams.Select(Expr.Ident).ToList() + ) { Proc = first.proc }, + new CallCmd(Token.NoToken, + second.proc.Name, + second.secondInParams.Select(Expr.Ident).ToList(), + second.secondOutParams.Select(Expr.Ident).ToList() + ) { Proc = second.proc } + }; + var block = new Block(Token.NoToken, "init", cmds, new ReturnCmd(Token.NoToken)); + + var secondInParamsFiltered = second.secondInParams.Where(v => linearTypeChecker.FindLinearKind(v) != LinearKind.LINEAR_IN); + IEnumerable linearityAssumes = Enumerable.Union( + DisjointnessExpr(first.firstOutParams.Union(secondInParamsFiltered), frame), + DisjointnessExpr(first.firstOutParams.Union(second.secondOutParams), frame)); + // TODO: add further disjointness expressions? + Ensures ensureCheck = new Ensures(first.proc.tok, false, Expr.Imp(Expr.And(linearityAssumes), transitionRelation), null) + { + ErrorData = $"Commutativity check between {first.proc.Name} and {second.proc.Name} failed" + }; + List ensures = new List { ensureCheck }; + + List inputs = Enumerable.Union(first.firstInParams, second.secondInParams).ToList(); + List outputs = Enumerable.Union(first.firstOutParams, second.secondOutParams).ToList(); + + AddChecker(checkerName, inputs, outputs, new List(), requires, ensures, new List { block }); } - private void CreateGatePreservationChecker(Program program, AtomicActionInfo first, AtomicActionInfo second) + private void CreateGatePreservationChecker(AtomicActionCopy first, AtomicActionCopy second) { - if (first.gateUsedGlobalVars.Intersect(second.modifiedGlobalVars).Count() == 0) + if (!first.gateUsedGlobalVars.Intersect(second.modifiedGlobalVars).Any()) return; - Tuple actionPair = new Tuple(first, second); - if (gatePreservationCheckerCache.Contains(actionPair)) + if (!gatePreservationCheckerCache.Add(Tuple.Create(first, second))) return; - gatePreservationCheckerCache.Add(actionPair); - List inputs = new List(); - inputs.AddRange(first.thatInParams); - inputs.AddRange(second.thisInParams); - List outputs = new List(); - outputs.AddRange(first.thatOutParams); - outputs.AddRange(second.thisOutParams); - List locals = new List(); - locals.AddRange(second.thisAction.LocVars); - List secondBlocks = CloneBlocks(second.thisAction.Blocks); HashSet frame = new HashSet(); frame.UnionWith(first.gateUsedGlobalVars); frame.UnionWith(second.gateUsedGlobalVars); frame.UnionWith(second.actionUsedGlobalVars); - List requires = DisjointnessRequires(program, first, second, frame); + + List requires = new List + { + DisjointnessRequires(first.firstInParams.Union(second.secondInParams).Where(v => linearTypeChecker.FindLinearKind(v) != LinearKind.LINEAR_OUT), frame) + }; List ensures = new List(); - foreach (AssertCmd assertCmd in first.thatGate) + foreach (AssertCmd assertCmd in second.secondGate) + requires.Add(new Requires(false, assertCmd.Expr)); + + IEnumerable linearityAssumes = DisjointnessExpr(first.firstInParams.Union(second.secondOutParams), frame); + foreach (AssertCmd assertCmd in first.firstGate) { requires.Add(new Requires(false, assertCmd.Expr)); - Ensures ensureCheck = new Ensures(assertCmd.tok, false, assertCmd.Expr, null); - ensureCheck.ErrorData = string.Format("Gate not preserved by {0}", second.proc.Name); + Ensures ensureCheck = new Ensures(assertCmd.tok, false, Expr.Imp(Expr.And(linearityAssumes), assertCmd.Expr), null) + { + ErrorData = $"Gate of {first.proc.Name} not preserved by {second.proc.Name}" + }; ensures.Add(ensureCheck); } - foreach (AssertCmd assertCmd in second.thisGate) - requires.Add(new Requires(false, assertCmd.Expr)); - string checkerName = string.Format("GatePreservationChecker_{0}_{1}", first.proc.Name, second.proc.Name); - List globalVars = new List(); - civlTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); - Procedure proc = new Procedure(Token.NoToken, checkerName, new List(), inputs, outputs, requires, globalVars, ensures); - Implementation impl = new Implementation(Token.NoToken, checkerName, new List(), inputs, outputs, locals, secondBlocks); - impl.Proc = proc; - this.decls.Add(impl); - this.decls.Add(proc); + string checkerName = $"GatePreservationChecker_{first.proc.Name}_{second.proc.Name}"; + + List inputs = Enumerable.Union(first.firstInParams, second.secondInParams).ToList(); + List outputs = Enumerable.Union(first.firstOutParams, second.secondOutParams).ToList(); + var block = new Block(Token.NoToken, "init", + new List + { + new CallCmd(Token.NoToken, + second.proc.Name, + second.secondInParams.Select(Expr.Ident).ToList(), + second.secondOutParams.Select(Expr.Ident).ToList() + ) { Proc = second.proc } + }, + new ReturnCmd(Token.NoToken)); + + AddChecker(checkerName, inputs, outputs, new List(), requires, ensures, new List { block }); } - private void CreateFailurePreservationChecker(Program program, AtomicActionInfo first, AtomicActionInfo second) + private void CreateFailurePreservationChecker(AtomicActionCopy first, AtomicActionCopy second) { - if (first.gateUsedGlobalVars.Intersect(second.modifiedGlobalVars).Count() == 0) + if (!first.gateUsedGlobalVars.Intersect(second.modifiedGlobalVars).Any()) return; - Tuple actionPair = new Tuple(first, second); - if (failurePreservationCheckerCache.Contains(actionPair)) + if (!failurePreservationCheckerCache.Add(Tuple.Create(first, second))) return; - failurePreservationCheckerCache.Add(actionPair); - List inputs = new List(); - inputs.AddRange(first.thatInParams); - inputs.AddRange(second.thisInParams); - List outputs = new List(); - outputs.AddRange(first.thatOutParams); - outputs.AddRange(second.thisOutParams); - List locals = new List(); - locals.AddRange(second.thisAction.LocVars); - List secondBlocks = CloneBlocks(second.thisAction.Blocks); HashSet frame = new HashSet(); frame.UnionWith(first.gateUsedGlobalVars); frame.UnionWith(second.gateUsedGlobalVars); frame.UnionWith(second.actionUsedGlobalVars); - List requires = DisjointnessRequires(program, first, second, frame); - Expr gateExpr = Expr.True; - foreach (AssertCmd assertCmd in first.thatGate) + + List requires = new List { - gateExpr = Expr.And(gateExpr, assertCmd.Expr); - gateExpr.Type = Type.Bool; - } - gateExpr = Expr.Not(gateExpr); - gateExpr.Type = Type.Bool; - requires.Add(new Requires(false, gateExpr)); - List ensures = new List(); - Ensures ensureCheck = new Ensures(false, gateExpr); - ensureCheck.ErrorData = string.Format("Gate failure of {0} not preserved by {1}", first.proc.Name, second.proc.Name); - ensures.Add(ensureCheck); - foreach (AssertCmd assertCmd in second.thisGate) + DisjointnessRequires(first.firstInParams.Union(second.secondInParams).Where(v => linearTypeChecker.FindLinearKind(v) != LinearKind.LINEAR_OUT), frame) + }; + Expr firstNegatedGate = Expr.Not(Expr.And(first.firstGate.Select(a => a.Expr))); + firstNegatedGate.Type = Type.Bool; // necessary? + requires.Add(new Requires(false, firstNegatedGate)); + foreach (AssertCmd assertCmd in second.secondGate) requires.Add(new Requires(false, assertCmd.Expr)); - string checkerName = string.Format("FailurePreservationChecker_{0}_{1}", first.proc.Name, second.proc.Name); - List globalVars = new List(); - civlTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); - Procedure proc = new Procedure(Token.NoToken, checkerName, new List(), inputs, outputs, requires, globalVars, ensures); - Implementation impl = new Implementation(Token.NoToken, checkerName, new List(), inputs, outputs, locals, secondBlocks); - impl.Proc = proc; - this.decls.Add(impl); - this.decls.Add(proc); + + IEnumerable linearityAssumes = DisjointnessExpr(first.firstInParams.Union(second.secondOutParams), frame); + Ensures ensureCheck = new Ensures(first.proc.tok, false, Expr.Imp(Expr.And(linearityAssumes), firstNegatedGate), null) + { + ErrorData = $"Gate failure of {first.proc.Name} not preserved by {second.proc.Name}" + }; + List ensures = new List { ensureCheck }; + + string checkerName = $"FailurePreservationChecker_{first.proc.Name}_{second.proc.Name}"; + + List inputs = Enumerable.Union(first.firstInParams, second.secondInParams).ToList(); + List outputs = Enumerable.Union(first.firstOutParams, second.secondOutParams).ToList(); + var block = new Block(Token.NoToken, "init", + new List + { + new CallCmd(Token.NoToken, + second.proc.Name, + second.secondInParams.Select(Expr.Ident).ToList(), + second.secondOutParams.Select(Expr.Ident).ToList() + ) { Proc = second.proc } + }, + new ReturnCmd(Token.NoToken)); + + AddChecker(checkerName, inputs, outputs, new List(), requires, ensures, new List { block }); } - private void CreateNonBlockingChecker(Program program, AtomicActionInfo second) + private void CreateNonBlockingChecker(AtomicActionCopy action) { - List inputs = new List(); - inputs.AddRange(second.thisInParams); + if (!action.HasAssumeCmd) return; + + string checkerName = $"NonBlockingChecker_{action.proc.Name}"; + Implementation impl = action.impl; HashSet frame = new HashSet(); - frame.UnionWith(second.gateUsedGlobalVars); - frame.UnionWith(second.actionUsedGlobalVars); - List requires = DisjointnessRequires(program, null, second, frame); - foreach (AssertCmd assertCmd in second.thisGate) + frame.UnionWith(action.gateUsedGlobalVars); + frame.UnionWith(action.actionUsedGlobalVars); + + List requires = new List + { + DisjointnessRequires(impl.InParams. + Where(v => linearTypeChecker.FindLinearKind(v) != LinearKind.LINEAR_OUT), frame) + }; + foreach (AssertCmd assertCmd in action.gate) { requires.Add(new Requires(false, assertCmd.Expr)); } - HashSet postExistVars = new HashSet(); - postExistVars.UnionWith(frame); - postExistVars.UnionWith(second.thisOutParams); - Expr ensuresExpr = (new TransitionRelationComputation(program, second, frame, postExistVars)).TransitionRelationCompute(); - List ensures = new List(); - Ensures ensureCheck = new Ensures(false, ensuresExpr); - ensureCheck.ErrorData = string.Format("{0} is blocking", second.proc.Name); - ensures.Add(ensureCheck); - - List blocks = new List(); - blocks.Add(new Block(Token.NoToken, "L", new List(), new ReturnCmd(Token.NoToken))); - string checkerName = string.Format("NonBlockingChecker_{0}", second.proc.Name); - List globalVars = new List(); - civlTypeChecker.SharedVariables.Iter(x => globalVars.Add(Expr.Ident(x))); - Procedure proc = new Procedure(Token.NoToken, checkerName, new List(), inputs, new List(), requires, globalVars, ensures); - Implementation impl = new Implementation(Token.NoToken, checkerName, new List(), inputs, new List(), new List(), blocks); - impl.Proc = proc; - this.decls.Add(impl); - this.decls.Add(proc); + + Expr nonBlockingExpr = TransitionRelationComputation. + Nonblocking(action, frame); + AssertCmd nonBlockingAssert = new AssertCmd(action.proc.tok, nonBlockingExpr) + { + ErrorData = $"Non-blocking check for {action.proc.Name} failed" + }; + + Block block = new Block(action.proc.tok, "L", new List { nonBlockingAssert }, + new ReturnCmd(Token.NoToken)); + + AddChecker(checkerName, new List(impl.InParams), new List(), + new List(), requires, new List(), new List { block }); } } -} \ No newline at end of file +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/NoninterferenceInstrumentation.cs boogie-2.4.1+dfsg/Source/Concurrency/NoninterferenceInstrumentation.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/NoninterferenceInstrumentation.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/NoninterferenceInstrumentation.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Boogie +{ + interface NoninterferenceInstrumentation + { + List NewLocalVars { get; } + List CreateAssumeCmds(Absy absy); + List CreateUpdatesToPermissionCollector(Absy absy); + List CreateInitCmds(Implementation impl); + List CreateYieldCheckerProcImpl(Implementation impl, IEnumerable> yields); + List CreateCallToYieldProc(); + } + + class NoneNoninterferenceInstrumentation : NoninterferenceInstrumentation + { + public List NewLocalVars => new List(); + + public List CreateAssumeCmds(Absy absy) + { + return new List(); + } + + public List CreateUpdatesToPermissionCollector(Absy absy) + { + return new List(); + } + + public List CreateInitCmds(Implementation impl) + { + return new List(); + } + + public List CreateYieldCheckerProcImpl(Implementation impl, IEnumerable> yields) + { + return new List(); + } + + public List CreateCallToYieldProc() + { + return new List(); + } + } + + class SomeNoninterferenceInstrumentation : NoninterferenceInstrumentation + { + private CivlTypeChecker civlTypeChecker; + private LinearTypeChecker linearTypeChecker; + private int layerNum; + private Dictionary absyMap; + private Dictionary oldGlobalMap; + private List newLocalVars; + private Dictionary domainNameToLocalVar; + private Procedure yieldProc; + + public SomeNoninterferenceInstrumentation( + CivlTypeChecker civlTypeChecker, + LinearTypeChecker linearTypeChecker, + int layerNum, + Dictionary absyMap, + Dictionary oldGlobalMap, + Procedure yieldProc) + { + this.civlTypeChecker = civlTypeChecker; + this.linearTypeChecker = linearTypeChecker; + this.layerNum = layerNum; + this.absyMap = absyMap; + this.oldGlobalMap = oldGlobalMap; + this.yieldProc = yieldProc; + this.newLocalVars = new List(); + this.domainNameToLocalVar = new Dictionary(); + { + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + Variable l = linearTypeChecker.LinearDomainAvailableLocal(domainName); + domainNameToLocalVar[domainName] = l; + newLocalVars.Add(l); + } + } + } + + public List NewLocalVars => newLocalVars; + + public List CreateUpdatesToPermissionCollector(Absy absy) + { + Dictionary domainNameToExpr = ComputeAvailableExprs(AvailableLinearVars(absy)); + List lhss = new List(); + List rhss = new List(); + foreach (var domainName in linearTypeChecker.linearDomains.Keys) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(domainNameToLocalVar[domainName]))); + rhss.Add(domainNameToExpr[domainName]); + } + var cmds = new List(); + if (lhss.Count > 0) + { + cmds.Add(new AssignCmd(Token.NoToken, lhss, rhss)); + } + return cmds; + } + + public List CreateInitCmds(Implementation impl) + { + Dictionary domainNameToExpr = ComputeAvailableExprs(impl.InParams.FindAll(x => linearTypeChecker.FindDomainName(x) != null)); + List lhss = new List(); + List rhss = new List(); + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(domainNameToLocalVar[domainName]))); + rhss.Add(domainNameToExpr[domainName]); + } + var initCmds = new List(); + if (lhss.Count > 0) + { + initCmds.Add(new AssignCmd(Token.NoToken, lhss, rhss)); + } + return initCmds; + } + + public List CreateAssumeCmds(Absy absy) + { + Dictionary domainNameToExpr = ComputeAvailableExprs(AvailableLinearVars(absy)); + List newCmds = new List(); + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + newCmds.Add(new AssumeCmd(Token.NoToken, + Expr.Eq(Expr.Ident(domainNameToLocalVar[domainName]), domainNameToExpr[domainName]))); + } + return newCmds; + } + + public List CreateYieldCheckerProcImpl( + Implementation impl, + IEnumerable> yields) + { + Dictionary map = new Dictionary(); + List locals = new List(); + List inputs = new List(); + + foreach (Variable local in impl.LocVars.Union(impl.InParams).Union(impl.OutParams)) + { + var copy = CopyLocal(local); + locals.Add(copy); + map[local] = Expr.Ident(copy); + } + + foreach (var domainName in linearTypeChecker.linearDomains.Keys) + { + var inParam = linearTypeChecker.LinearDomainInFormal(domainName); + inputs.Add(inParam); + map[linearTypeChecker.domainNameToHoleVar[domainName]] = Expr.Ident(inParam); + } + + Dictionary oldLocalMap = new Dictionary(); + Dictionary assumeMap = new Dictionary(map); + foreach (Variable g in civlTypeChecker.sharedVariables) + { + var copy = OldLocalLocal(g); + locals.Add(copy); + oldLocalMap[g] = Expr.Ident(copy); + Formal f = OldGlobalFormal(g); + inputs.Add(f); + assumeMap[g] = Expr.Ident(f); + } + + Substitution assumeSubst = Substituter.SubstitutionFromHashtable(assumeMap); + Substitution oldSubst = Substituter.SubstitutionFromHashtable(oldLocalMap); + Substitution subst = Substituter.SubstitutionFromHashtable(map); + List yieldCheckerBlocks = new List(); + List labels = new List(); + List labelTargets = new List(); + Block yieldCheckerBlock = new Block(Token.NoToken, "exit", new List(), new ReturnCmd(Token.NoToken)); + labels.Add(yieldCheckerBlock.Label); + labelTargets.Add(yieldCheckerBlock); + yieldCheckerBlocks.Add(yieldCheckerBlock); + int yieldCount = 0; + foreach (var cs in yields) + { + List newCmds = new List(); + foreach (var predCmd in cs) + { + var newExpr = Substituter.ApplyReplacingOldExprs(assumeSubst, oldSubst, predCmd.Expr); + newCmds.Add(new AssumeCmd(Token.NoToken, newExpr)); + } + + foreach (var predCmd in cs) + { + if (predCmd is AssertCmd) + { + var newExpr = Substituter.ApplyReplacingOldExprs(subst, oldSubst, predCmd.Expr); + AssertCmd assertCmd = new AssertCmd(predCmd.tok, newExpr, predCmd.Attributes); + assertCmd.ErrorData = "Non-interference check failed"; + newCmds.Add(assertCmd); + } + + /* + Disjointness assumes injected by LinearTypeChecker are dropped now because the + previous loop has already substituted the old global state in these assumes. + It would be unsound to have these assumes on the current global state. + */ + } + + newCmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + yieldCheckerBlock = new Block(Token.NoToken, "L" + yieldCount++, newCmds, new ReturnCmd(Token.NoToken)); + labels.Add(yieldCheckerBlock.Label); + labelTargets.Add(yieldCheckerBlock); + yieldCheckerBlocks.Add(yieldCheckerBlock); + } + + yieldCheckerBlocks.Insert(0, + new Block(Token.NoToken, "enter", new List(), new GotoCmd(Token.NoToken, labels, labelTargets))); + + // Create the yield checker procedure + var yieldCheckerName = $"Impl_YieldChecker_{impl.Name}"; + var yieldCheckerProc = new Procedure(Token.NoToken, yieldCheckerName, impl.TypeParameters, inputs, + new List(), new List(), new List(), new List()); + CivlUtil.AddInlineAttribute(yieldCheckerProc); + + // Create the yield checker implementation + var yieldCheckerImpl = new Implementation(Token.NoToken, yieldCheckerName, impl.TypeParameters, inputs, + new List(), locals, yieldCheckerBlocks); + yieldCheckerImpl.Proc = yieldCheckerProc; + CivlUtil.AddInlineAttribute(yieldCheckerImpl); + return new List{ yieldCheckerProc, yieldCheckerImpl }; + } + + public List CreateCallToYieldProc() + { + List exprSeq = new List(); + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + exprSeq.Add(Expr.Ident(domainNameToLocalVar[domainName])); + } + + foreach (Variable g in civlTypeChecker.sharedVariables) + { + exprSeq.Add(Expr.Ident(oldGlobalMap[g])); + } + + CallCmd yieldCallCmd = new CallCmd(Token.NoToken, yieldProc.Name, exprSeq, new List()); + yieldCallCmd.Proc = yieldProc; + return new List {yieldCallCmd}; + } + + private IEnumerable AvailableLinearVars(Absy absy) + { + HashSet availableVars = + new HashSet(linearTypeChecker.AvailableLinearVars(absyMap[absy])); + + // A bit hackish, since GlobalVariableLayerRange and LocalVariableIntroLayer return maximum layer range + // respectively minimum layer if called on non-global respectively non-local variable. + availableVars.RemoveWhere(v => + !civlTypeChecker.GlobalVariableLayerRange(v).Contains(layerNum) || + layerNum < civlTypeChecker.LocalVariableIntroLayer(v) + ); + + return availableVars; + } + + private Dictionary ComputeAvailableExprs(IEnumerable availableLinearVars) + { + Dictionary domainNameToExpr = new Dictionary(); + foreach (var domainName in linearTypeChecker.linearDomains.Keys) + { + var domain = linearTypeChecker.linearDomains[domainName]; + var expr = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapConstBool), new Expr[] {Expr.False}); + expr.Resolve(new ResolutionContext(null)); + expr.Typecheck(new TypecheckingContext(null)); + domainNameToExpr[domainName] = expr; + } + + foreach (Variable v in availableLinearVars) + { + var domainName = linearTypeChecker.FindDomainName(v); + var domain = linearTypeChecker.linearDomains[domainName]; + Expr ie = new NAryExpr(Token.NoToken, new FunctionCall(domain.collectors[v.TypedIdent.Type]), + new List {Expr.Ident(v)}); + var expr = new NAryExpr(Token.NoToken, new FunctionCall(domain.mapOrBool), + new List {ie, domainNameToExpr[domainName]}); + expr.Resolve(new ResolutionContext(null)); + expr.Typecheck(new TypecheckingContext(null)); + domainNameToExpr[domainName] = expr; + } + + return domainNameToExpr; + } + + private Formal OldGlobalFormal(Variable v) + { + return new Formal(Token.NoToken, + new TypedIdent(Token.NoToken, $"og_global_old_{v.Name}", v.TypedIdent.Type), true); + } + + private LocalVariable OldLocalLocal(Variable v) + { + return new LocalVariable(Token.NoToken, + new TypedIdent(Token.NoToken, $"og_local_old_{v.Name}", v.TypedIdent.Type)); + } + + private LocalVariable CopyLocal(Variable v) + { + return new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, v.Name, v.TypedIdent.Type)); + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/Program.cs boogie-2.4.1+dfsg/Source/Concurrency/Program.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/Program.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/Program.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Microsoft.Boogie -{ - public class Concurrency - { - public static void Transform(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker) - { - List originalDecls = new List(); - Program program = linearTypeChecker.program; - foreach (var decl in program.TopLevelDeclarations) - { - Procedure proc = decl as Procedure; - if (proc != null && civlTypeChecker.procToActionInfo.ContainsKey(proc)) - { - originalDecls.Add(proc); - continue; - } - Implementation impl = decl as Implementation; - if (impl != null && civlTypeChecker.procToActionInfo.ContainsKey(impl.Proc)) - { - originalDecls.Add(impl); - } - } - - List decls = new List(); - if (!CommandLineOptions.Clo.TrustAtomicityTypes) - { - MoverCheck.AddCheckers(linearTypeChecker, civlTypeChecker, decls); - } - CivlRefinement.AddCheckers(linearTypeChecker, civlTypeChecker, decls); - foreach (Declaration decl in decls) - { - decl.Attributes = CivlRefinement.RemoveYieldsAttribute(decl.Attributes); - } - program.RemoveTopLevelDeclarations(x => originalDecls.Contains(x)); - program.AddTopLevelDeclarations(decls); - } - - } -} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/RefinementInstrumentation.cs boogie-2.4.1+dfsg/Source/Concurrency/RefinementInstrumentation.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/RefinementInstrumentation.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/RefinementInstrumentation.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,358 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Boogie +{ + interface RefinementInstrumentation + { + List NewLocalVars { get; } + List CreateAssumeCmds(); + List CreateFinalAssertCmds(); + List CreateAssertCmds(); + List CreateUpdateCmds(); + List CreateUpdatesToOldOutputVars(); + List CreateInitCmds(); + List CreateYieldingLoopHeaderInitCmds(Block header); + List CreateYieldingLoopHeaderAssertCmds(Block header); + } + + class NoneRefinementInstrumentation : RefinementInstrumentation + { + public List NewLocalVars => new List(); + + public List CreateAssumeCmds() + { + return new List(); + } + + public List CreateFinalAssertCmds() + { + return new List(); + } + + public List CreateAssertCmds() + { + return new List(); + } + + public List CreateUpdateCmds() + { + return new List(); + } + + public List CreateUpdatesToOldOutputVars() + { + return new List(); + } + + public List CreateInitCmds() + { + return new List(); + } + + public List CreateYieldingLoopHeaderInitCmds(Block header) + { + return new List(); + } + + public List CreateYieldingLoopHeaderAssertCmds(Block header) + { + return new List(); + } + } + + class SomeRefinementInstrumentation : RefinementInstrumentation + { + private Dictionary oldGlobalMap; + private Dictionary oldOutputMap; + private List newLocalVars; + private Variable pc; + private Variable ok; + private Expr alpha; + private Expr beta; + private Dictionary pcsForYieldingLoopsHeaders; + private Dictionary oksForYieldingLoopHeaders; + + private Dictionary transitionRelationCache; + + public SomeRefinementInstrumentation( + CivlTypeChecker civlTypeChecker, + Implementation impl, + Procedure originalProc, + Dictionary oldGlobalMap, + HashSet yieldingLoopHeaders) + { + newLocalVars = new List(); + YieldingProc yieldingProc = civlTypeChecker.procToYieldingProc[originalProc]; + int layerNum = yieldingProc.upperLayer; + pc = Pc(); + newLocalVars.Add(pc); + ok = Ok(); + newLocalVars.Add(ok); + + this.transitionRelationCache = new Dictionary(); + + this.oldGlobalMap = new Dictionary(); + foreach (Variable v in civlTypeChecker.sharedVariables) + { + var layerRange = civlTypeChecker.GlobalVariableLayerRange(v); + if (layerRange.lowerLayerNum <= yieldingProc.upperLayer && yieldingProc.upperLayer < layerRange.upperLayerNum) + { + this.oldGlobalMap[v] = oldGlobalMap[v]; + } + } + + Dictionary foroldMap = new Dictionary(); + foreach (Variable g in civlTypeChecker.sharedVariables) + { + foroldMap[g] = Expr.Ident(oldGlobalMap[g]); + } + if (yieldingProc is ActionProc actionProc) + { + // The parameters of an atomic action come from the implementation that denotes the atomic action specification. + // To use the transition relation computed below in the context of the yielding procedure of the refinement check, + // we need to substitute the parameters. + AtomicActionCopy atomicActionCopy = actionProc.refinedAction.layerToActionCopy[layerNum + 1]; + Implementation atomicActionImpl = atomicActionCopy.impl; + Dictionary alwaysMap = new Dictionary(); + for (int i = 0; i < atomicActionImpl.InParams.Count; i++) + { + alwaysMap[atomicActionImpl.InParams[i]] = Expr.Ident(impl.InParams[i]); + } + + for (int i = 0; i < atomicActionImpl.OutParams.Count; i++) + { + alwaysMap[atomicActionImpl.OutParams[i]] = Expr.Ident(impl.OutParams[i]); + } + + Substitution always = Substituter.SubstitutionFromHashtable(alwaysMap); + Substitution forold = Substituter.SubstitutionFromHashtable(foroldMap); + Expr betaExpr = GetTransitionRelation(atomicActionCopy); + beta = Substituter.ApplyReplacingOldExprs(always, forold, betaExpr); + Expr alphaExpr = Expr.And(atomicActionCopy.gate.Select(g => g.Expr)); + alphaExpr.Type = Type.Bool; + alpha = Substituter.Apply(always, alphaExpr); + } + else + { + beta = Expr.And(this.oldGlobalMap.Keys.Select(v => Expr.Eq(Expr.Ident(v), foroldMap[v]))); + alpha = Expr.True; + } + + oldOutputMap = new Dictionary(); + foreach (Variable f in impl.OutParams) + { + LocalVariable copy = Old(f); + newLocalVars.Add(copy); + this.oldOutputMap[f] = copy; + } + + pcsForYieldingLoopsHeaders = new Dictionary(); + oksForYieldingLoopHeaders = new Dictionary(); + foreach (Block header in yieldingLoopHeaders) + { + var pcForYieldingLoopHeader = PcForYieldingLoopHeader(header); + newLocalVars.Add(pcForYieldingLoopHeader); + pcsForYieldingLoopsHeaders[header] = pcForYieldingLoopHeader; + var okForYieldingLoopHeader = OkForYieldingLoopHeader(header); + newLocalVars.Add(okForYieldingLoopHeader); + oksForYieldingLoopHeaders[header] = okForYieldingLoopHeader; + } + } + + private Expr GetTransitionRelation(AtomicActionCopy atomicActionCopy) + { + if (!transitionRelationCache.ContainsKey(atomicActionCopy)) + { + transitionRelationCache[atomicActionCopy] = + TransitionRelationComputation. + Refinement(atomicActionCopy, new HashSet(this.oldGlobalMap.Keys)); + } + return transitionRelationCache[atomicActionCopy]; + } + + public List NewLocalVars => newLocalVars; + + public List CreateAssumeCmds() + { + var cmds = new List(); + // assume pc || alpha(i, g); + Expr assumeExpr = Expr.Or(Expr.Ident(pc), alpha); + assumeExpr.Type = Type.Bool; + cmds.Add(new AssumeCmd(Token.NoToken, assumeExpr)); + return cmds; + } + + public List CreateFinalAssertCmds() + { + var cmds = CreateAssertCmds(); + AssertCmd assertCmd = new AssertCmd(Token.NoToken, Expr.Ident(ok)); + assertCmd.ErrorData = "Failed to execute atomic action before procedure return"; + cmds.Add(assertCmd); + return cmds; + } + + public List CreateAssertCmds() + { + var cmds = new List(); + cmds.Add(CreateSkipOrBetaAssertCmd()); + cmds.Add(CreateSkipAssertCmd()); + return cmds; + } + + public List CreateInitCmds() + { + var cmds = new List(); + List lhss = new List(); + List rhss = new List(); + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(pc))); + rhss.Add(Expr.False); + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(ok))); + rhss.Add(Expr.False); + cmds.Add(new AssignCmd(Token.NoToken, lhss, rhss)); + cmds.AddRange(CreateUpdatesToOldOutputVars()); + return cmds; + } + + public List CreateUpdateCmds() + { + // pc, ok := g_old == g ==> pc, ok || beta(i, g_old, o, g); + Expr aa = OldEqualityExprForGlobals(); + List pcUpdateLHS = new List + { + new SimpleAssignLhs(Token.NoToken, Expr.Ident(pc)), + new SimpleAssignLhs(Token.NoToken, Expr.Ident(ok)) + }; + List pcUpdateRHS = new List( + new Expr[] + { + Expr.Imp(aa, Expr.Ident(pc)), + Expr.Or(Expr.Ident(ok), beta) + }); + foreach (Expr e in pcUpdateRHS) + { + e.Typecheck(new TypecheckingContext(null)); + } + return new List {new AssignCmd(Token.NoToken, pcUpdateLHS, pcUpdateRHS)}; + } + + public List CreateUpdatesToOldOutputVars() + { + List lhss = new List(); + List rhss = new List(); + foreach (Variable o in oldOutputMap.Keys) + { + lhss.Add(new SimpleAssignLhs(Token.NoToken, Expr.Ident(oldOutputMap[o]))); + rhss.Add(Expr.Ident(o)); + } + var cmds = new List(); + if (lhss.Count > 0) + { + cmds.Add(new AssignCmd(Token.NoToken, lhss, rhss)); + } + return cmds; + } + + public List CreateYieldingLoopHeaderInitCmds(Block header) + { + var newCmds = new List(); + var pcForYieldingLoopHeader = pcsForYieldingLoopsHeaders[header]; + var okForYieldingLoopHeader = oksForYieldingLoopHeaders[header]; + var assignCmd = new AssignCmd(Token.NoToken, + new List + { + new SimpleAssignLhs(Token.NoToken, Expr.Ident(pcForYieldingLoopHeader)), + new SimpleAssignLhs(Token.NoToken, Expr.Ident(okForYieldingLoopHeader)) + }, + new List {Expr.Ident(pc), Expr.Ident(ok)}); + newCmds.Add(assignCmd); + return newCmds; + } + + public List CreateYieldingLoopHeaderAssertCmds(Block header) + { + var newCmds = new List(); + var pcForYieldingLoopHeader = pcsForYieldingLoopsHeaders[header]; + var pcAssertCmd = new AssertCmd(header.tok, Expr.Eq(Expr.Ident(pcForYieldingLoopHeader), Expr.Ident(pc))); + pcAssertCmd.ErrorData = "Specification state must not change for transitions ending in loop headers"; + newCmds.Add(pcAssertCmd); + var okForYieldingLoopHeader = oksForYieldingLoopHeaders[header]; + var okAssertCmd = new AssertCmd(header.tok, Expr.Imp(Expr.Ident(okForYieldingLoopHeader), Expr.Ident(ok))); + okAssertCmd.ErrorData = "Specification state must not change for transitions ending in loop headers"; + newCmds.Add(okAssertCmd); + return newCmds; + } + + private AssertCmd CreateSkipOrBetaAssertCmd() + { + // assert pc || g_old == g || beta(i, g_old, o, g); + var aa = OldEqualityExprForGlobals(); + var assertExpr = Expr.Or(Expr.Ident(pc), Expr.Or(aa, beta)); + assertExpr.Typecheck(new TypecheckingContext(null)); + var skipOrBetaAssertCmd = new AssertCmd(Token.NoToken, assertExpr); + skipOrBetaAssertCmd.ErrorData = "Transition invariant violated in initial state"; + return skipOrBetaAssertCmd; + } + + private AssertCmd CreateSkipAssertCmd() + { + // assert pc ==> o_old == o && g_old == g; + Expr bb = OldEqualityExpr(); + var assertExpr = Expr.Imp(Expr.Ident(pc), bb); + assertExpr.Typecheck(new TypecheckingContext(null)); + AssertCmd skipAssertCmd = new AssertCmd(Token.NoToken, assertExpr); + skipAssertCmd.ErrorData = "Transition invariant violated in final state"; + return skipAssertCmd; + } + + private Expr OldEqualityExpr() + { + Expr bb = OldEqualityExprForGlobals(); + foreach (Variable o in oldOutputMap.Keys) + { + bb = Expr.And(bb, Expr.Eq(Expr.Ident(o), Expr.Ident(oldOutputMap[o]))); + bb.Type = Type.Bool; + } + return bb; + } + + private Expr OldEqualityExprForGlobals() + { + Expr bb = Expr.True; + foreach (Variable g in oldGlobalMap.Keys) + { + bb = Expr.And(bb, Expr.Eq(Expr.Ident(g), Expr.Ident(oldGlobalMap[g]))); + bb.Type = Type.Bool; + } + return bb; + } + + private LocalVariable Old(Variable v) + { + return new LocalVariable(Token.NoToken, + new TypedIdent(Token.NoToken, $"og_old_{v.Name}", v.TypedIdent.Type)); + } + + private LocalVariable Pc() + { + return new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_pc", Type.Bool)); + } + + private LocalVariable Ok() + { + return new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "og_ok", Type.Bool)); + } + + private LocalVariable PcForYieldingLoopHeader(Block header) + { + return new LocalVariable(Token.NoToken, + new TypedIdent(Token.NoToken, $"og_pc_{header.Label}", Type.Bool)); + } + + private LocalVariable OkForYieldingLoopHeader(Block header) + { + return new LocalVariable(Token.NoToken, + new TypedIdent(Token.NoToken, $"og_ok_{header.Label}", Type.Bool)); + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/SimulationRelation.cs boogie-2.4.1+dfsg/Source/Concurrency/SimulationRelation.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/SimulationRelation.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/SimulationRelation.cs 2019-10-25 18:17:05.000000000 +0000 @@ -1,11 +1,18 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using Microsoft.Boogie.GraphUtil; namespace Microsoft.Boogie { + /// + /// Implements the simulation relation algorithm from the paper: + /// Monika Rauch Henzinger, Thomas A. Henzinger, Peter W. Kopke + /// Computing Simulations on Finite and Infinite Graphs + /// FOCS 1995 + /// + /// node type of graph A + /// node type of graph B + /// type of edge labels public class SimulationRelation { class Graph diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/TransitionRelationComputation.cs boogie-2.4.1+dfsg/Source/Concurrency/TransitionRelationComputation.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/TransitionRelationComputation.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/TransitionRelationComputation.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,736 @@ +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using System.ComponentModel; +using System; + +namespace Microsoft.Boogie +{ + public enum AtomicActionCopyKind + { + FIRST, SECOND, NORMAL + } + + public class AtomicActionCopyAdapter + { + public readonly AtomicActionCopy action; + public readonly AtomicActionCopyKind copyType; + + public AtomicActionCopyAdapter(AtomicActionCopy action, AtomicActionCopyKind copyType) + { + this.action = action; + this.copyType = copyType; + } + + private T PassByKind(T normalValue, T firstValue, T secondValue) + { + switch (copyType) + { + case AtomicActionCopyKind.FIRST: + return firstValue; + case AtomicActionCopyKind.SECOND: + return secondValue; + case AtomicActionCopyKind.NORMAL: + return normalValue; + default: + throw new InvalidEnumArgumentException(); + } + } + + public List Gate + { + get + { + return PassByKind(action.gate, action.firstGate, action.secondGate); + } + } + + public List Blocks + { + get + { + return PassByKind(action.impl.Blocks, action.firstAction.Blocks, + action.secondAction.Blocks); + } + } + + public List InParams + { + get + { + return PassByKind(action.impl.InParams, action.firstInParams, + action.secondInParams); + } + } + + public List OutParams + { + get + { + return PassByKind(action.impl.OutParams, action.firstOutParams, + action.secondOutParams); + } + } + + public List LocVars + { + get + { + return PassByKind(action.impl.LocVars, action.firstAction.LocVars, + action.secondAction.LocVars); + } + } + + public IEnumerable Params + { + get + { + return InParams.Union(OutParams); + } + } + + public string Prefix + { + get + { + return PassByKind("", "first_", "second_"); + } + } + } + + public class TransitionRelationComputation + { + internal readonly AtomicActionCopyAdapter first, second; + internal readonly HashSet frame; + internal readonly Dictionary> globalVarToWitnesses; + internal readonly bool ignorePostState; + + internal readonly string messagePrefix; + internal readonly CheckingContext checkingContext; + + private List path; + private int transferIndex; // from first to second action + + private List pathTranslations; + + private TransitionRelationComputation( + AtomicActionCopyAdapter first, AtomicActionCopyAdapter second, + IEnumerable frame, List witnesses, bool ignorePostState, + string messagePrefix) + { + this.first = first; + this.second = second; + this.frame = new HashSet(frame); + this.ignorePostState = ignorePostState; + + this.messagePrefix = messagePrefix; + this.checkingContext = new CheckingContext(null); + + this.pathTranslations = new List(); + this.globalVarToWitnesses = new Dictionary>(); + if (witnesses != null) + { + foreach (var witness in witnesses) + { + var gVar = witness.globalVar; + if (!globalVarToWitnesses.ContainsKey(gVar)) + { + globalVarToWitnesses[gVar] = new List(); + } + globalVarToWitnesses[gVar].Add(witness); + } + } + } + + private static Expr ComputeTransitionRelation( + AtomicActionCopyAdapter first, AtomicActionCopyAdapter second, + IEnumerable frame, List witnesses, bool ignorePostState, + string messagePrefix) + { + var trc = new TransitionRelationComputation(first, second, frame, witnesses, ignorePostState, messagePrefix); + trc.EnumeratePaths(); + var transitionRelation = Expr.Or(trc.pathTranslations); + + ResolutionContext rc = new ResolutionContext(null) + { + StateMode = ResolutionContext.State.Two + }; + transitionRelation.Resolve(rc); + transitionRelation.Typecheck(new TypecheckingContext(null)); + + return transitionRelation; + } + + public static Expr Commutativity(AtomicActionCopy first, AtomicActionCopy second, + HashSet frame, List witnesses) + { + return ComputeTransitionRelation( + new AtomicActionCopyAdapter(first, AtomicActionCopyKind.SECOND), + new AtomicActionCopyAdapter(second, AtomicActionCopyKind.FIRST), + frame, witnesses, false, + string.Format("Transition relation of {0} ∘ {1}", first.proc.Name, second.proc.Name)); + } + + public static Expr Refinement(AtomicActionCopy action, HashSet frame) + { + return ComputeTransitionRelation( + new AtomicActionCopyAdapter(action, AtomicActionCopyKind.NORMAL), + null, frame, null, false, + string.Format("Transition relation of {0}", action.proc.Name)); + } + + public static Expr Nonblocking(AtomicActionCopy action, HashSet frame) + { + return ComputeTransitionRelation( + new AtomicActionCopyAdapter(action, AtomicActionCopyKind.NORMAL), + null, frame, null, true, + string.Format("Nonblocking expression of {0}", action.proc.Name)); + } + + private void EnumeratePaths() + { + path = new List(); + Debug.Assert(path.Count == 0); + EnumeratePathsRec(first.Blocks[0], true); + Debug.Assert(path.Count == 0); + } + + private void EnumeratePathsRec(Block b, bool inFirst) + { + int pathSizeAtEntry = path.Count; + + foreach (Cmd cmd in b.Cmds) + { + path.Add(cmd); + } + if (b.TransferCmd is ReturnCmd) + { + if (inFirst && second != null) + { + transferIndex = path.Count; + EnumeratePathsRec(second.Blocks[0], false); + } + else + { + AddPath(); + } + } + else + { + GotoCmd gotoCmd = b.TransferCmd as GotoCmd; + foreach (Block target in gotoCmd.labelTargets) + { + EnumeratePathsRec(target, inFirst); + } + } + + Debug.Assert(path.Count >= pathSizeAtEntry); + path.RemoveRange(pathSizeAtEntry, path.Count - pathSizeAtEntry); + } + + private void AddPath() + { + var pathTranslation = new PathTranslation(this, path); + pathTranslations.Add(pathTranslation.TransitionRelationExpr); + + if (CommandLineOptions.Clo.WarnNotEliminatedVars) + { + var quantifiedVars = pathTranslation.GetQuantifiedOriginalVariables(); + if (quantifiedVars.Any()) + { + checkingContext.Warning(Token.NoToken, + string.Format("{0}: could not eliminate variables {{{1}}} on some path", + messagePrefix, string.Join(", ", quantifiedVars))); + } + } + } + + internal class PathTranslation + { + private readonly List cmds; + private readonly TransitionRelationComputation transitionRelationComputer; + private readonly HashSet allInParams, allOutParams, allLocVars, frame; + private readonly AtomicActionCopyAdapter first, second; + + // Used when second != null + // TODO: Add some comments + private Dictionary frameIntermediateCopy; + + private List newCmds; + private Dictionary[] varCopies; + private Dictionary copyToOriginalVar; + private Dictionary varLastCopyId; + private HashSet definedVariables; + private Dictionary varToExpr; + private List pathExprs; + private List witnessedTransitionRelations; + + private Dictionary existsVarMap; + + internal Expr TransitionRelationExpr; + + private const string copierFormat = "{0}#{1}"; + + internal PathTranslation(TransitionRelationComputation transitionRelationComputer, + List cmds) + { + this.cmds = cmds; + this.transitionRelationComputer = transitionRelationComputer; + this.frame = transitionRelationComputer.frame; + this.first = transitionRelationComputer.first; + this.second = transitionRelationComputer.second; + + allInParams = new HashSet(first.InParams); + allOutParams = new HashSet(first.OutParams); + allLocVars = new HashSet(first.LocVars); + frameIntermediateCopy = new Dictionary(); + if (IsJoint()) + { + allInParams.UnionWith(second.InParams); + allOutParams.UnionWith(second.OutParams); + allLocVars.UnionWith(second.LocVars); + } + + SetupVarCopies(); + IntroduceIntermediateVars(); + EliminateIntermediateVariables(); + if (IsJoint()) + { + EliminateWithIntermediateState(); + } + ComputeTransitionRelationExpr(); + } + + private void EliminateWithIntermediateState() + { + Debug.Assert(IsJoint()); + + var remainingIntermediateFrame = frameIntermediateCopy.Values.Except(varToExpr.Keys); + while (TryElimination(remainingIntermediateFrame)) { } + + while (TryElimination(remainingIntermediateFrame. + Intersect(IntermediateFrameWithWitnesses))) { } + // TODO: Generate warning for variables without any witness functions + } + + private bool IsJoint() + { + return second != null; + } + + private IEnumerable UsedVariables + { + get + { + return allInParams. + Union(allOutParams). + Union(allLocVars). + Union(frame).Distinct(); + } + } + + private IEnumerable FrameWithWitnesses + { + get { + return frame.Intersect( + transitionRelationComputer.globalVarToWitnesses.Keys); + } + } + + private IEnumerable IntermediateFrameWithWitnesses + { + get + { + return FrameWithWitnesses. + Select(v => frameIntermediateCopy[v]); + } + } + + private void SetupVarCopies() + { + varCopies = new Dictionary[cmds.Count + 1]; + foreach (int i in Enumerable.Range(0, cmds.Count + 1)) + varCopies[i] = new Dictionary(); + + copyToOriginalVar = new Dictionary(); + + varLastCopyId = new Dictionary(); + foreach (var v in UsedVariables) + { + MakeNewCopy(v); + } + } + + private Dictionary GetVarCopiesFromIds(Dictionary varCopyId) + { + return varCopyId.ToDictionary(kvp => kvp.Key, kvp => varCopies[kvp.Value][kvp.Key]); + } + + private void PopulateIntermediateFrameCopy() + { + frameIntermediateCopy = GetVarCopiesFromIds(varLastCopyId). + Where(kvp => frame.Contains(kvp.Key)). + ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } + + private void IntroduceIntermediateVars() + { + var oldSub = Substituter.SubstitutionFromHashtable(GetPreStateVars(). + ToDictionary(v => v, v => Expr.Ident(varCopies[0][v]))); + newCmds = new List(); + for (int k = 0; k < cmds.Count; k++) + { + if (IsJoint() && k == transitionRelationComputer.transferIndex) + { + PopulateIntermediateFrameCopy(); + oldSub = Substituter.SubstitutionFromHashtable(GetPreStateVars(). + ToDictionary(v => v, v => Expr.Ident(varCopies[varLastCopyId[v]][v]))); + } + Cmd cmd = cmds[k]; + if (cmd is AssignCmd) + { + AssignCmd assignCmd = ((AssignCmd)cmd).AsSimpleAssignCmd; + var preState = GetVarCopiesFromIds(varLastCopyId); + foreach (var v in assignCmd.Lhss) + { + MakeNewCopy(v.DeepAssignedVariable); + } + var postState = GetVarCopiesFromIds(varLastCopyId); + + Dictionary lhsMap = postState, rhsMap = preState; + if (QKeyValue.FindBoolAttribute(assignCmd.Attributes, CivlAttributes.BACKWARD)) + { + lhsMap = preState; + rhsMap = postState; + } + + var rhsSub = Substituter.SubstitutionFromHashtable( + rhsMap.ToDictionary( + kvp => kvp.Key, kvp => Expr.Ident(kvp.Value) as Expr + )); + + List lhss = assignCmd.Lhss.Select(x => (AssignLhs)new SimpleAssignLhs(Token.NoToken, + new IdentifierExpr(Token.NoToken, lhsMap[x.DeepAssignedVariable]))).ToList(); + List rhss = assignCmd.Rhss.Select(x => + Substituter.ApplyReplacingOldExprs(rhsSub, oldSub, x)).ToList(); + + newCmds.Add(new AssignCmd(Token.NoToken, lhss, rhss, assignCmd.Attributes)); + } + else if (cmd is AssumeCmd) + { + var sub = Substituter.SubstitutionFromHashtable( + GetVarCopiesFromIds(varLastCopyId).ToDictionary( + kvp => kvp.Key, kvp => Expr.Ident(kvp.Value) as Expr + )); + newCmds.Add(new AssumeCmd(cmd.tok, + Substituter.ApplyReplacingOldExprs(sub, oldSub, ((AssumeCmd)cmd).Expr))); + } + else if (cmd is HavocCmd havocCmd) + { + foreach (var v in havocCmd.Vars) + { + MakeNewCopy(v.Decl); + } + } + else + { + Debug.Assert(false); + } + } + // TODO: Add note on this + if (!IsJoint() || cmds.Count == transitionRelationComputer.transferIndex) + PopulateIntermediateFrameCopy(); + } + + private void MakeNewCopy(Variable v) + { + varLastCopyId[v] = varLastCopyId.ContainsKey(v) ? varLastCopyId[v] + 1 : 0; + int id = varLastCopyId[v]; + var copyVar = new Formal( + Token.NoToken, + new TypedIdent(Token.NoToken, string.Format(copierFormat, v.Name, id), v.TypedIdent.Type), + false, null); + varCopies[id][v] = copyVar; + copyToOriginalVar[copyVar] = v; + } + + private void SetDefinedVariables() + { + definedVariables = new HashSet(); + foreach (var v in GetPreStateVars()) + { + definedVariables.Add(varCopies[0][v]); + } + if (!IgnorePostState) + { + foreach (var v in GetPostStateVars()) + { + definedVariables.Add(varCopies[varLastCopyId[v]][v]); + } + } + } + + private IEnumerable GetPostStateVars() + { + return frame.Union(allOutParams).Distinct(); + } + + private IEnumerable GetPreStateVars() + { + return frame.Union(allInParams).Distinct(); + } + + private static Cmd ApplyOnRhss(Substitution sub, Cmd cmd) + { + if (cmd is AssignCmd assignCmd) + { + return new AssignCmd(cmd.tok, + assignCmd.Lhss, + assignCmd.Rhss.Select(x => Substituter.Apply(sub, x)).ToList(), + assignCmd.Attributes); + } + else { return Substituter.Apply(sub, cmd); } + } + + private void EliminateIntermediateVariables() + { + SetDefinedVariables(); + varToExpr = new Dictionary(); + foreach (var v in definedVariables) + { + varToExpr[v] = Expr.Ident(v); + } + + while (TryElimination(new HashSet())) { } + + while (TryElimination(allLocVars.Select(v => varCopies[0][v]))) { } + + if (IgnorePostState) + { + while (TryElimination(GetPostStateVars())) { } + } + } + + private bool TryElimination(IEnumerable extraDefinedVariables) + { + bool changed = false; + var remainingCmds = new List(); + foreach (var cmd in newCmds) + { + if (cmd is AssignCmd assignCmd) + { + var lhss = new List(); + var rhss = new List(); + for (int k = 0; k < assignCmd.Lhss.Count; k++) + { + var lhs = assignCmd.Lhss[k]; + var rhs = assignCmd.Rhss[k]; + Variable assignedVar = lhs.DeepAssignedVariable; + + var allDefinedVars = varToExpr.Keys.Union(extraDefinedVariables); + if (!allDefinedVars.Contains(assignedVar) && + !VariableCollector.Collect(rhs).Intersect(AllIntroducedVariables). + Except(allDefinedVars).Any()) + { + varToExpr[assignedVar] = rhs; + changed = true; + } + else + { + lhss.Add(lhs); + rhss.Add(rhs); + } + } + if (lhss.Any()) + { + remainingCmds.Add(new AssignCmd(cmd.tok, lhss, rhss, assignCmd.Attributes)); + } + } + else if (cmd is AssumeCmd) + { + remainingCmds.Add(cmd); + } + } + Substitution sub = Substituter.SubstitutionFromHashtable(varToExpr); + newCmds = remainingCmds.Select(cmd => ApplyOnRhss(sub, cmd)).ToList(); + return changed; + } + + private void ComputeTransitionRelationExpr() + { + CalculatePathExpression(); + AddBoundVariablesForRemainingVars(); + ReplacePreOrPostStateVars(); + TransitionRelationExpr = Expr.And(pathExprs); + if (IsJoint()) + { + ComputeWitnessedTransitionRelationExprs(); + if (witnessedTransitionRelations.Count > 0) + { + TransitionRelationExpr = Expr.Or(witnessedTransitionRelations); + } + } + if (existsVarMap.Any()) + { + TransitionRelationExpr = new ExistsExpr(Token.NoToken, + existsVarMap.Values.ToList(), TransitionRelationExpr); + } + } + + private void ComputeWitnessedTransitionRelationExprs() + { + witnessedTransitionRelations = new List(); + Dictionary> varToWitnesses = FrameWithWitnesses. + Where(x => NotEliminatedVars.Contains(frameIntermediateCopy[x])). + ToDictionary( + x => frameIntermediateCopy[x], + x => transitionRelationComputer.globalVarToWitnesses[(GlobalVariable)x]); + foreach (var witnessSet in varToWitnesses.Values.CartesianProduct()) + { + Dictionary witnessSubst = new Dictionary(); + foreach (Tuple pair in + Enumerable.Zip(varToWitnesses.Keys, witnessSet, Tuple.Create)) + { + WitnessFunction witnessFunction = pair.Item2; + List args = new List(); + foreach (var arg in witnessFunction.InputArgs) + { + Expr expr = null; + switch (arg.Kind) + { + case WitnessFunction.InputArgumentKind.FIRST_ARG: + // TODO: Add note on the reason of using second + expr = Expr.Ident(second.Params. + First(x => x.Name == second.Prefix + arg.Name)); + break; + case WitnessFunction.InputArgumentKind.SECOND_ARG: + expr = Expr.Ident(first.Params. + First(x => x.Name == first.Prefix + arg.Name)); + break; + case WitnessFunction.InputArgumentKind.PRE_STATE: + expr = ExprHelper.Old(Expr.Ident( + frame.First(x => x.Name == arg.Name))); + break; + case WitnessFunction.InputArgumentKind.POST_STATE: + expr = Expr.Ident(frame.First(x => x.Name == arg.Name)); + break; + default: + Debug.Assert(false); + break; + } + args.Add(expr); + } + witnessSubst[pair.Item1] = ExprHelper.FunctionCall( + witnessFunction.function, args.ToArray() + ); + } + var subst = Substituter.SubstitutionFromHashtable(witnessSubst); + witnessedTransitionRelations.Add( + Substituter.Apply(subst, TransitionRelationExpr)); + } + } + + private void ReplacePreOrPostStateVars() + { + var preStateSub = GetPreStateVars(). + ToDictionary(v => varCopies[0][v], + v => new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v))); + + var frameCopiesSub = preStateSub; + if (!IgnorePostState) + { + var postStateSub = GetPostStateVars(). + ToDictionary(v => varCopies[varLastCopyId[v]][v], v => Expr.Ident(v)); + + var notModifiedVars = new HashSet(preStateSub.Keys.Intersect(postStateSub.Keys)); + foreach (var v in notModifiedVars) + { + pathExprs.Add(Expr.Eq(preStateSub[v], postStateSub[v])); + postStateSub.Remove(v); + } + + frameCopiesSub = frameCopiesSub.Union(postStateSub).ToDictionary(k => k.Key, v => v.Value); + } + + var finalSub = Substituter.SubstitutionFromHashtable(frameCopiesSub); + pathExprs = pathExprs.Select(x => Substituter.Apply(finalSub, x)).ToList(); + } + + private bool IgnorePostState + { + get + { + return transitionRelationComputer.ignorePostState; + } + } + + private IEnumerable NotEliminatedVars + { + get + { + return newCmds. + SelectMany(cmd => VariableCollector.Collect(cmd)). + Intersect(AllIntroducedVariables). + Except(varToExpr.Keys); + } + } + + private IEnumerable AllIntroducedVariables + { + get + { + return varCopies.SelectMany(x => x.Values); + } + } + + private void AddBoundVariablesForRemainingVars() + { + var remainingVars = NotEliminatedVars.Except(IntermediateFrameWithWitnesses); + existsVarMap = new Dictionary(); + foreach (var v in remainingVars) + { + existsVarMap[v] = new BoundVariable(Token.NoToken, + new TypedIdent(Token.NoToken, v.Name, v.TypedIdent.Type)); + } + var varMap = existsVarMap.ToDictionary(kvp => kvp.Key, kvp => Expr.Ident(kvp.Value) as Expr); + var varSubst = Substituter.SubstitutionFromHashtable(varMap); + pathExprs = pathExprs.Select(x => Substituter.Apply(varSubst, x)).ToList(); + } + + private void CalculatePathExpression() + { + pathExprs = new List(); + foreach (var cmd in newCmds.Where(x => x is AssumeCmd).Cast()) + { + FlattenAnd(cmd.Expr, pathExprs); + } + foreach (AssignCmd cmd in newCmds.Where(x => x is AssignCmd).Cast()) + { + for (int k = 0; k < cmd.Lhss.Count; k++) + { + pathExprs.Add(Expr.Eq(Expr.Ident(cmd.Lhss[k].DeepAssignedVariable), cmd.Rhss[k])); + } + } + + var varMap = varToExpr.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + var varSubst = Substituter.SubstitutionFromHashtable(varMap); + pathExprs = pathExprs.Select(x => Substituter.Apply(varSubst, x)).ToList(); + } + + private static void FlattenAnd(Expr x, List xs) + { + if (x is NAryExpr naryExpr && naryExpr.Fun.FunctionName == "&&") + { + FlattenAnd(naryExpr.Args[0], xs); + FlattenAnd(naryExpr.Args[1], xs); + } + else { xs.Add(x); } + } + + internal IEnumerable GetQuantifiedOriginalVariables() + { + return existsVarMap.Keys.Select(x => copyToOriginalVar[x]).Distinct(); + } + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/Witnesses.cs boogie-2.4.1+dfsg/Source/Concurrency/Witnesses.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/Witnesses.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/Witnesses.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,355 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Microsoft.Boogie +{ + public class WitnessFunction + { + public enum InputArgumentKind + { + PRE_STATE, + POST_STATE, + FIRST_ARG, + SECOND_ARG + } + + public struct InputArgument + { + public readonly InputArgumentKind Kind; + public readonly string Name; + + public InputArgument(InputArgumentKind kind, string name) + { + this.Kind = kind; + this.Name = name; + } + } + + public readonly Function function; + public readonly GlobalVariable globalVar; + public readonly AtomicAction firstAction; + public readonly AtomicAction secondAction; + public readonly List layers; + public List InputArgs { get; private set; } + + public WitnessFunction(Function function, GlobalVariable globalVar, + AtomicAction firstAction, AtomicAction secondAction, List layers) + { + this.function = function; + this.globalVar = globalVar; + this.firstAction = firstAction; + this.secondAction = secondAction; + this.layers = layers; + this.InputArgs = new List(); + } + + internal void AddInputMap(InputArgumentKind argumentKind, string name) + { + InputArgs.Add(new InputArgument(argumentKind, name)); + } + } + + class WitnessFunctionVisitor : ReadOnlyVisitor + { + private readonly CivlTypeChecker ctc; + internal List allWitnessFunctions; + + public WitnessFunctionVisitor(CivlTypeChecker ctc) + { + this.ctc = ctc; + allWitnessFunctions = new List(); + } + + public override Function VisitFunction(Function node) + { + for (QKeyValue kv = node.Attributes; kv != null; kv = kv.Next) + { + if (kv.Key != CivlAttributes.WITNESS) + continue; + if (kv.Params.Count == 1 && kv.Params[0] is string witnessAttribute) + { + int parserErrorCount = WitnessAttributeParser.Parse(ctc, node, witnessAttribute, + out WitnessFunction witnessFunction); + if (parserErrorCount == 0 && + WitnessFunctionChecker.Check(node, ctc, witnessFunction) == 0) + { + allWitnessFunctions.Add(witnessFunction); + } + } + else + { + ctc.Error(node, "Witness attribute has to be a string."); + } + } + return base.VisitFunction(node); + } + + private class WitnessFunctionChecker + { + // TODO: Move these to a better place + private const string FirstProcInputPrefix = "first_"; + private const string SecondProcInputPrefix = "second_"; + private const string PostStateSuffix = "'"; + private readonly Function node; + private readonly CivlTypeChecker ctc; + private readonly WitnessFunction witnessFunction; + + private int errorCount; + + private WitnessFunctionChecker(Function node, CivlTypeChecker ctc, WitnessFunction witnessFunction) + { + this.node = node; + this.ctc = ctc; + this.witnessFunction = witnessFunction; + this.errorCount = 0; + Check(); + } + + internal static int Check(Function node, CivlTypeChecker ctc, WitnessFunction witnessFunction) + { + var checker = new WitnessFunctionChecker(node, ctc, witnessFunction); + return checker.errorCount; + } + + private void Check() + { + CheckOutputVariable(); + CheckAtomicActionsLayers(); + CheckArguments(); + } + + private void CheckAtomicActionsLayers() + { + AtomicAction[] actions = { witnessFunction.firstAction, witnessFunction.secondAction }; + foreach (var action in actions) + { + CheckLayerExistence(action.layerRange, action.proc.Name); + } + } + + private void CheckLayerExistence(LayerRange layerRange, string nodeName) + { + foreach (int layer in witnessFunction.layers) + { + if (!layerRange.Contains(layer)) + { + Error(string.Format("{0} does not exist at layer {1}", nodeName, layer)); + } + } + } + + private void CheckOutputVariable() + { + if (node.OutParams.Count != 1) + { + Error("Witness functions must have only one output"); + return; + } + var v = witnessFunction.globalVar; + CheckGlobalArg(v.TypedIdent.Type, v.Name); + } + + private void CheckArguments() + { + for (int k = 0; k < node.InParams.Count; k++) + { + var arg = node.InParams[k]; + string name = node.InParams[k].Name; + Type type = arg.TypedIdent.Type; + WitnessFunction.InputArgumentKind argumentKind; + if (name.StartsWith(FirstProcInputPrefix, StringComparison.Ordinal)) + { + name = name.Substring(FirstProcInputPrefix.Length); + argumentKind = WitnessFunction.InputArgumentKind.FIRST_ARG; + CheckArg(witnessFunction.firstAction, type, name); + } + else if (name.StartsWith(SecondProcInputPrefix, StringComparison.Ordinal)) + { + name = name.Substring(SecondProcInputPrefix.Length); + argumentKind = WitnessFunction.InputArgumentKind.SECOND_ARG; + CheckArg(witnessFunction.secondAction, type, name); + } + else + { + if (name.EndsWith(PostStateSuffix, StringComparison.Ordinal)) + { + name = name.Substring(0, name.Length - 1); + argumentKind = WitnessFunction.InputArgumentKind.POST_STATE; + } + else + { + argumentKind = WitnessFunction.InputArgumentKind.PRE_STATE; + } + CheckGlobalArg(type, name); + } + witnessFunction.AddInputMap(argumentKind, name); + } + } + + private void CheckGlobalArg(Type type, string name) + { + GlobalVariable globalVar = ctc.sharedVariables.Find(v => v.Name == name); + if (globalVar is null) + { + Error("No matching global variable named " + name); + return; + } + + var gType = globalVar.TypedIdent.Type; + if (!type.Equals(gType)) + { + Error(string.Format( + "Type mismatch for variable {0}. Expected {1} found {2}", + name, gType, type)); + } + CheckLayerExistence(ctc.GlobalVariableLayerRange(globalVar), globalVar.Name); + } + + private void CheckArg(AtomicAction action, Type type, string name) + { + var proc = action.proc; + var v = proc.InParams.Union(proc.OutParams). + FirstOrDefault(i => i.Name == name && i.TypedIdent.Type.Equals(type)); + if (v is null) + { + Error(string.Format("No parameter {0}:{1} found in {2}", + name, type, action.proc.Name)); + } + } + + private void Error(string msg) + { + ctc.Error(node, msg); + errorCount++; + } + } + + private class WitnessAttributeParser + { + private readonly CivlTypeChecker ctc; + private readonly Function node; + private readonly string[] parts; + + internal GlobalVariable globalVar; + internal AtomicAction firstAction; + internal AtomicAction secondAction; + internal List layers; + + private int parseIndex; + private string ld; + + private int errorCount; + + private WitnessAttributeParser(CivlTypeChecker ctc, Function node, string witnessAttr) + { + this.ctc = ctc; + this.node = node; + + char[] separators = { ' ', '\t' }; + parts = witnessAttr.Split(separators, + StringSplitOptions.RemoveEmptyEntries); + + this.parseIndex = 0; + this.errorCount = 0; + Parse(); + } + + internal static int Parse(CivlTypeChecker ctc, Function node, string witnessAttr, + out WitnessFunction witnessFunction) + { + var parser = new WitnessAttributeParser(ctc, node, witnessAttr); + witnessFunction = new WitnessFunction(node, parser.globalVar, + parser.firstAction, parser.secondAction, parser.layers); + return parser.errorCount; + } + + private void Parse() + { + ParseGlobalVar(); + ParseProc(out firstAction); + ParseProc(out secondAction); + ParseLayers(); + Debug.Assert(!TryNext()); + } + + private bool TryNext(string msg = null) + { + if (parseIndex >= parts.Length) + { + if (msg != null) { Error(msg); } + ld = null; + return false; + } + ld = parts[parseIndex++]; + return true; + } + + private void ParseLayers() + { + layers = new List(); + if (TryNext("Expected layer number")) + { + do + { + AddLayer(); + } while (TryNext()); + } + } + + private void AddLayer() + { + if (int.TryParse(ld, out int layerNum)) + { + layers.Add(layerNum); + } + else + { + Error(string.Format("Expected integer layer number but received {0}", ld)); + } + } + + private void ParseProc(out AtomicAction action) + { + TryNext("Expected procedure name."); + Procedure proc = ctc.program.FindProcedure(ld); + if (proc is null) + { + Error("No procedure found with name of " + ld); + action = null; + } + else + { + if (!ctc.procToAtomicAction.ContainsKey(proc)) + { + ctc.checkingContext.Warning(node, + string.Format( + "Procedure {0} does not have a mover type, witness function is ignored.", + proc.Name)); + // TODO: Add notes on this + errorCount++; + action = null; + } + else { action = ctc.procToAtomicAction[proc]; } + } + } + + private void ParseGlobalVar() + { + TryNext("Expected global variable name"); + globalVar = ctc.sharedVariables.Find(v => v.Name == ld); + if (globalVar is null) + { + Error("No global variable found with name of " + ld); + } + } + + private void Error(string msg) + { + ctc.Error(node, msg); + errorCount++; + } + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/YieldingProcChecker.cs boogie-2.4.1+dfsg/Source/Concurrency/YieldingProcChecker.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/YieldingProcChecker.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/YieldingProcChecker.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Boogie +{ + public class YieldingProcChecker + { + public static void AddCheckers(LinearTypeChecker linearTypeChecker, CivlTypeChecker civlTypeChecker, List decls) + { + Program program = linearTypeChecker.program; + + // Skip procedures do not completely disappear (because of output parameters). + // We create dummy implementations with empty body. + Dictionary procToSkipProcDummy = new Dictionary(); + foreach(SkipProc skipProc in civlTypeChecker.procToYieldingProc.Values.OfType()) + { + Procedure proc = (Procedure)skipProc.proc.Clone(); + proc.Name = $"skip_dummy_{proc.Name}"; + proc.Requires = new List(); + proc.Ensures = new List(); + proc.Modifies = new List(); + Block newInitBlock = new Block(Token.NoToken, "_init", new List(), new ReturnCmd(Token.NoToken)); + List newBlocks = new List { newInitBlock }; + Implementation impl = new Implementation(Token.NoToken, proc.Name, proc.TypeParameters, proc.InParams, proc.OutParams, new List(), newBlocks); + impl.Proc = proc; + CivlUtil.AddInlineAttribute(proc); + CivlUtil.AddInlineAttribute(impl); + decls.Add(proc); + decls.Add(impl); + procToSkipProcDummy.Add(skipProc.proc, proc); + } + + // Generate the refinement checks for every layer + foreach (int layerNum in civlTypeChecker.allRefinementLayers) + { + if (CommandLineOptions.Clo.TrustLayersDownto <= layerNum || layerNum <= CommandLineOptions.Clo.TrustLayersUpto) continue; + + YieldingProcDuplicator duplicator = new YieldingProcDuplicator(civlTypeChecker, linearTypeChecker, layerNum, procToSkipProcDummy); + + // We can not simply call VisitProgram, because it does some resolving of calls + // that is not necessary here (and actually fails). + foreach (Procedure proc in program.Procedures) + { + duplicator.VisitProcedure(proc); + } + foreach (Implementation impl in program.Implementations) + { + duplicator.VisitImplementation(impl); + } + decls.AddRange(duplicator.Collect()); + } + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/YieldingProcDuplicator.cs boogie-2.4.1+dfsg/Source/Concurrency/YieldingProcDuplicator.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/YieldingProcDuplicator.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/YieldingProcDuplicator.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,310 @@ +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; + +namespace Microsoft.Boogie +{ + public class YieldingProcDuplicator : Duplicator + { + private CivlTypeChecker civlTypeChecker; + private LinearTypeChecker linearTypeChecker; + private Dictionary procToSkipProcDummy; + private YieldingProc enclosingYieldingProc; + + private int layerNum; + private Dictionary procMap; /* Original -> Duplicate */ + private Dictionary absyMap; /* Duplicate -> Original */ + private Dictionary implMap; /* Duplicate -> Original */ + private HashSet yieldingProcs; + + public YieldingProcDuplicator(CivlTypeChecker civlTypeChecker, LinearTypeChecker linearTypeChecker, + int layerNum, Dictionary procToSkipProcDummy) + { + this.civlTypeChecker = civlTypeChecker; + this.linearTypeChecker = linearTypeChecker; + this.layerNum = layerNum; + this.procToSkipProcDummy = procToSkipProcDummy; + + this.procMap = new Dictionary(); + this.absyMap = new Dictionary(); + this.implMap = new Dictionary(); + this.yieldingProcs = new HashSet(); + } + + public override Procedure VisitProcedure(Procedure node) + { + if (!civlTypeChecker.procToYieldingProc.ContainsKey(node)) + return node; + if (!procMap.ContainsKey(node)) + { + YieldingProc yieldingProc = civlTypeChecker.procToYieldingProc[node]; + + if (layerNum > yieldingProc.upperLayer) + { + if (yieldingProc is ActionProc actionProc) + { + // yielding procedure already transformed to atomic action + var action = actionProc.refinedAction; + if (action.layerRange.Contains(layerNum)) + return action.layerToActionCopy[layerNum].proc; + else + return node; + } + else if (yieldingProc is SkipProc) + { + // (calls to) skip procedures do not completely disappear because of output variables + return procToSkipProcDummy[yieldingProc.proc]; + } + else if (yieldingProc is MoverProc) + { + // mover procedure does not exist on this layer any more + return node; + } + } + + Procedure proc = (Procedure) node.Clone(); + proc.Name = $"{node.Name}_{layerNum}"; + proc.InParams = this.VisitVariableSeq(node.InParams); + proc.OutParams = this.VisitVariableSeq(node.OutParams); + proc.Requires = this.VisitRequiresSeq(node.Requires); + proc.Ensures = this.VisitEnsuresSeq(node.Ensures); + if (yieldingProc is MoverProc moverProc && yieldingProc.upperLayer == layerNum) + { + proc.Modifies = moverProc.modifiedGlobalVars.Select(g => Expr.Ident(g)).ToList(); + } + else + { + proc.Modifies = civlTypeChecker.sharedVariableIdentifiers; + yieldingProcs.Add(proc); + } + + procMap[node] = proc; + } + + return procMap[node]; + } + + public override List VisitRequiresSeq(List requiresSeq) + { + requiresSeq = base.VisitRequiresSeq(requiresSeq); + requiresSeq.RemoveAll(req => req.Condition.Equals(Expr.True)); + return requiresSeq; + } + + public override List VisitEnsuresSeq(List ensuresSeq) + { + ensuresSeq = base.VisitEnsuresSeq(ensuresSeq); + ensuresSeq.RemoveAll(ens => ens.Condition.Equals(Expr.True)); + return ensuresSeq; + } + + public override Requires VisitRequires(Requires node) + { + Requires requires = base.VisitRequires(node); + if (node.Free) + return requires; + if (!civlTypeChecker.absyToLayerNums[node].Contains(layerNum)) + requires.Condition = Expr.True; + return requires; + } + + public override Ensures VisitEnsures(Ensures node) + { + Ensures ensures = base.VisitEnsures(node); + if (node.Free) + return ensures; + if (!civlTypeChecker.absyToLayerNums[node].Contains(layerNum)) + ensures.Condition = Expr.True; + return ensures; + } + + public override Implementation VisitImplementation(Implementation impl) + { + if (!civlTypeChecker.procToYieldingProc.ContainsKey(impl.Proc)) + return impl; + + enclosingYieldingProc = civlTypeChecker.procToYieldingProc[impl.Proc]; + if (layerNum > enclosingYieldingProc.upperLayer) + return impl; + + Implementation newImpl = base.VisitImplementation(impl); + newImpl.Name = newImpl.Proc.Name; + implMap[newImpl] = impl; + return newImpl; + } + + public override YieldCmd VisitYieldCmd(YieldCmd node) + { + YieldCmd yieldCmd = base.VisitYieldCmd(node); + absyMap[yieldCmd] = node; + return yieldCmd; + } + + public override Block VisitBlock(Block node) + { + Block block = base.VisitBlock(node); + absyMap[block] = node; + return block; + } + + public override Cmd VisitAssertCmd(AssertCmd node) + { + AssertCmd assertCmd = (AssertCmd) base.VisitAssertCmd(node); + if (!civlTypeChecker.absyToLayerNums[node].Contains(layerNum)) + assertCmd.Expr = Expr.True; + return assertCmd; + } + + public override Cmd VisitCallCmd(CallCmd call) + { + CallCmd newCall = (CallCmd) base.VisitCallCmd(call); + newCall.Proc = VisitProcedure(newCall.Proc); + + if (newCall.IsAsync) + { + Debug.Assert(civlTypeChecker.procToYieldingProc.ContainsKey(call.Proc)); + YieldingProc yieldingProc = civlTypeChecker.procToYieldingProc[call.Proc]; + Debug.Assert(yieldingProc.IsLeftMover); + if (yieldingProc.upperLayer < layerNum || + (yieldingProc is MoverProc && yieldingProc.upperLayer == layerNum)) + { + newCall.IsAsync = false; + } + + if (enclosingYieldingProc.upperLayer == layerNum && yieldingProc.upperLayer > layerNum) + { + ActionProc actionProc = (ActionProc) yieldingProc; + newCall.Proc = actionProc.addPendingAsyncProc; + newCall.IsAsync = false; + } + } + + newCall.callee = newCall.Proc.Name; + absyMap[newCall] = call; + return newCall; + } + + public override Cmd VisitParCallCmd(ParCallCmd node) + { + ParCallCmd parCallCmd = (ParCallCmd) base.VisitParCallCmd(node); + absyMap[parCallCmd] = node; + return parCallCmd; + } + + public override List VisitCmdSeq(List cmdSeq) + { + List visitedCmdSeq = base.VisitCmdSeq(cmdSeq); + List newCmdSeq = new List(); + for (int i = 0; i < visitedCmdSeq.Count; i++) + { + Cmd originalCmd = cmdSeq[i]; + Cmd visitedCmd = visitedCmdSeq[i]; + + if (originalCmd is CallCmd) + { + ProcessCallCmd((CallCmd) originalCmd, (CallCmd) visitedCmd, newCmdSeq); + } + else if (originalCmd is ParCallCmd) + { + ProcessParCallCmd((ParCallCmd) originalCmd, (ParCallCmd) visitedCmd, newCmdSeq); + } + else + { + newCmdSeq.Add(visitedCmd); + } + } + + newCmdSeq.RemoveAll(cmd => (cmd is AssertCmd assertCmd && assertCmd.Expr.Equals(Expr.True)) || + (cmd is AssumeCmd assumeCmd && assumeCmd.Expr.Equals(Expr.True))); + return newCmdSeq; + } + + // We only have to check the gate of a called atomic action (via an assert) at the creation layer of the caller. + // In all layers below we just establish invariants which do not require the gates to be checked and we can inject is as an assume. + private void InjectGate(ActionProc calledActionProc, CallCmd callCmd, List newCmds) + { + if (calledActionProc.upperLayer >= layerNum) return; + AtomicActionCopy atomicActionCopy = calledActionProc.refinedAction.layerToActionCopy[layerNum]; + if (atomicActionCopy.gate.Count == 0) return; + + Dictionary map = new Dictionary(); + for (int i = 0; i < calledActionProc.proc.InParams.Count; i++) + { + // Parameters come from the implementation that defines the atomic action + map[atomicActionCopy.impl.InParams[i]] = callCmd.Ins[i]; + } + + Substitution subst = Substituter.SubstitutionFromHashtable(map); + + // Important: Do not remove CommentCmd! + // It separates the injected gate from yield assertions in CollectAndDesugarYields. + newCmds.Add(new CommentCmd("<<< injected gate")); + foreach (AssertCmd assertCmd in atomicActionCopy.gate) + { + if (layerNum == enclosingYieldingProc.upperLayer) + newCmds.Add((AssertCmd) Substituter.Apply(subst, assertCmd)); + else + newCmds.Add(new AssumeCmd(assertCmd.tok, Substituter.Apply(subst, assertCmd.Expr))); + } + + newCmds.Add(new CommentCmd("injected gate >>>")); + } + + private void ProcessCallCmd(CallCmd originalCallCmd, CallCmd callCmd, List newCmds) + { + Procedure originalProc = originalCallCmd.Proc; + + if (civlTypeChecker.procToIntroductionProc.ContainsKey(originalProc)) + { + if (!civlTypeChecker.CallExists(originalCallCmd, enclosingYieldingProc.upperLayer, layerNum)) + return; + } + else if (civlTypeChecker.procToYieldingProc[originalProc] is ActionProc actionProc) + { + Debug.Assert(layerNum <= enclosingYieldingProc.upperLayer); + InjectGate(actionProc, callCmd, newCmds); + } + + newCmds.Add(callCmd); + } + + private void ProcessParCallCmd(ParCallCmd originalParCallCmd, ParCallCmd parCallCmd, List newCmds) + { + int maxCalleeLayerNum = 0; + foreach (CallCmd iter in originalParCallCmd.CallCmds) + { + int calleeLayerNum = civlTypeChecker.procToYieldingProc[iter.Proc].upperLayer; + if (calleeLayerNum > maxCalleeLayerNum) + maxCalleeLayerNum = calleeLayerNum; + } + + if (layerNum > maxCalleeLayerNum) + { + for (int i = 0; i < parCallCmd.CallCmds.Count; i++) + { + ProcessCallCmd(originalParCallCmd.CallCmds[i], parCallCmd.CallCmds[i], newCmds); + absyMap[parCallCmd.CallCmds[i]] = originalParCallCmd; + } + } + else + { + newCmds.Add(parCallCmd); + } + } + + public List Collect() + { + var decls = new List(); + decls.AddRange(procMap.Values); + decls.AddRange(implMap.Keys); + decls.AddRange(YieldingProcInstrumentation.TransformImplementations( + civlTypeChecker, + linearTypeChecker, + layerNum, + absyMap, + implMap, + yieldingProcs)); + return decls; + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/YieldingProcInstrumentation.cs boogie-2.4.1+dfsg/Source/Concurrency/YieldingProcInstrumentation.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/YieldingProcInstrumentation.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/YieldingProcInstrumentation.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,628 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics.Contracts; +using Microsoft.Boogie.GraphUtil; + +namespace Microsoft.Boogie +{ + class YieldingProcInstrumentation + { + public static List TransformImplementations( + CivlTypeChecker civlTypeChecker, + LinearTypeChecker linearTypeChecker, + int layerNum, + Dictionary absyMap, + Dictionary implMap, + HashSet yieldingProcs) + { + var yieldingProcInstrumentation = new YieldingProcInstrumentation(civlTypeChecker, linearTypeChecker, layerNum, + absyMap, implMap, yieldingProcs); + foreach (var kv in implMap) + { + var impl = kv.Value; + var newImpl = kv.Key; + var proc = civlTypeChecker.procToYieldingProc[impl.Proc]; + if (!(proc is MoverProc && proc.upperLayer == layerNum)) + { + yieldingProcInstrumentation.TransformImpl(newImpl); + } + } + + List decls = new List(yieldingProcInstrumentation.yieldCheckerDecls); + foreach (Procedure proc in yieldingProcInstrumentation.asyncAndParallelCallDesugarings.Values) + { + decls.Add(proc); + } + decls.Add(yieldingProcInstrumentation.yieldProc); + decls.Add(yieldingProcInstrumentation.YieldImpl()); + return decls; + } + + private YieldingProcInstrumentation( + CivlTypeChecker civlTypeChecker, + LinearTypeChecker linearTypeChecker, + int layerNum, + Dictionary absyMap, + Dictionary implMap, + HashSet yieldingProcs) + { + this.civlTypeChecker = civlTypeChecker; + this.linearTypeChecker = linearTypeChecker; + this.layerNum = layerNum; + this.absyMap = absyMap; + this.implMap = implMap; + this.yieldingProcs = yieldingProcs; + asyncAndParallelCallDesugarings = new Dictionary(); + yieldCheckerDecls = new List(); + + List inputs = new List(); + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + inputs.Add(linearTypeChecker.LinearDomainInFormal(domainName)); + } + + foreach (Variable g in civlTypeChecker.sharedVariables) + { + inputs.Add(OldGlobalFormal(g)); + } + + yieldProc = new Procedure(Token.NoToken, $"og_yield_{layerNum}", new List(), + inputs, new List(), new List(), new List(), new List()); + CivlUtil.AddInlineAttribute(yieldProc); + } + + private CivlTypeChecker civlTypeChecker; + private LinearTypeChecker linearTypeChecker; + private int layerNum; + private Dictionary absyMap; + private Dictionary implMap; + private HashSet yieldingProcs; + + private Dictionary asyncAndParallelCallDesugarings; + private List yieldCheckerDecls; + private Procedure yieldProc; + + private GlobalSnapshotInstrumentation globalSnapshotInstrumentation; + private RefinementInstrumentation refinementInstrumentation; + private NoninterferenceInstrumentation noninterferenceInstrumentation; + + private Implementation YieldImpl() + { + List inputs = new List(); + foreach (string domainName in linearTypeChecker.linearDomains.Keys) + { + inputs.Add(linearTypeChecker.LinearDomainInFormal(domainName)); + } + + foreach (Variable g in civlTypeChecker.sharedVariables) + { + inputs.Add(OldGlobalFormal(g)); + } + + List blocks = new List(); + TransferCmd transferCmd = new ReturnCmd(Token.NoToken); + if (yieldCheckerDecls.Count > 0) + { + List blockTargets = new List(); + List labelTargets = new List(); + int labelCount = 0; + foreach (Procedure proc in yieldCheckerDecls.OfType()) + { + List exprSeq = new List(); + foreach (Variable v in inputs) + { + exprSeq.Add(Expr.Ident(v)); + } + + CallCmd callCmd = new CallCmd(Token.NoToken, proc.Name, exprSeq, new List()); + callCmd.Proc = proc; + string label = $"L_{labelCount++}"; + Block block = new Block(Token.NoToken, label, new List {callCmd}, + new ReturnCmd(Token.NoToken)); + labelTargets.Add(label); + blockTargets.Add(block); + blocks.Add(block); + } + + transferCmd = new GotoCmd(Token.NoToken, labelTargets, blockTargets); + } + + blocks.Insert(0, new Block(Token.NoToken, "enter", new List(), transferCmd)); + + var yieldImpl = new Implementation(Token.NoToken, yieldProc.Name, new List(), inputs, + new List(), new List(), blocks); + yieldImpl.Proc = yieldProc; + CivlUtil.AddInlineAttribute(yieldImpl); + return yieldImpl; + } + + private Formal OldGlobalFormal(Variable v) + { + return new Formal(Token.NoToken, + new TypedIdent(Token.NoToken, $"og_global_old_{v.Name}", v.TypedIdent.Type), true); + } + + private void TransformImpl(Implementation impl) + { + HashSet yieldingLoopHeaders; + Graph graph = ComputeYieldingLoopHeaders(impl, out yieldingLoopHeaders); + + // initialize globalSnapshotInstrumentation + globalSnapshotInstrumentation = new GlobalSnapshotInstrumentation(civlTypeChecker); + + // initialize refinementInstrumentation + Procedure originalProc = implMap[impl].Proc; + YieldingProc yieldingProc = civlTypeChecker.procToYieldingProc[originalProc]; + if (yieldingProc.upperLayer == this.layerNum) + { + refinementInstrumentation = new SomeRefinementInstrumentation( + civlTypeChecker, + impl, + originalProc, + globalSnapshotInstrumentation.OldGlobalMap, + yieldingLoopHeaders); + } + else + { + refinementInstrumentation = new NoneRefinementInstrumentation(); + } + + // initialize noninterferenceInstrumentation + if (CommandLineOptions.Clo.TrustNonInterference) + { + noninterferenceInstrumentation = new NoneNoninterferenceInstrumentation(); + } + else + { + noninterferenceInstrumentation = new SomeNoninterferenceInstrumentation(civlTypeChecker, + linearTypeChecker, + layerNum, absyMap, globalSnapshotInstrumentation.OldGlobalMap, yieldProc); + } + + DesugarConcurrency(impl, graph, yieldingLoopHeaders); + + impl.LocVars.AddRange(globalSnapshotInstrumentation.NewLocalVars); + impl.LocVars.AddRange(refinementInstrumentation.NewLocalVars); + impl.LocVars.AddRange(noninterferenceInstrumentation.NewLocalVars); + } + + private Block AddInitialBlock(Implementation impl) + { + var initCmds = new List(); + initCmds.AddRange(globalSnapshotInstrumentation.CreateInitCmds()); + initCmds.AddRange(refinementInstrumentation.CreateInitCmds()); + initCmds.AddRange(noninterferenceInstrumentation.CreateInitCmds(impl)); + var gotoCmd = new GotoCmd(Token.NoToken, new List {impl.Blocks[0].Label}, + new List {impl.Blocks[0]}); + return new Block(Token.NoToken, "og_init", initCmds, gotoCmd); + } + + private Graph ComputeYieldingLoopHeaders(Implementation impl, out HashSet yieldingLoopHeaders) + { + Graph graph; + impl.PruneUnreachableBlocks(); + impl.ComputePredecessorsForBlocks(); + graph = Program.GraphFromImpl(impl); + graph.ComputeLoops(); + if (!graph.Reducible) + { + throw new Exception("Irreducible flow graphs are unsupported."); + } + + yieldingLoopHeaders = new HashSet(); + IEnumerable sortedHeaders = graph.SortHeadersByDominance(); + foreach (Block header in sortedHeaders) + { + if (yieldingLoopHeaders.Any(x => graph.DominatorMap.DominatedBy(x, header))) + { + yieldingLoopHeaders.Add(header); + } + else if (IsYieldingHeader(graph, header)) + { + yieldingLoopHeaders.Add(header); + } + } + + return graph; + } + + private bool IsYieldingHeader(Graph graph, Block header) + { + foreach (Block backEdgeNode in graph.BackEdgeNodes(header)) + { + foreach (Block x in graph.NaturalLoops(header, backEdgeNode)) + { + foreach (Cmd cmd in x.Cmds) + { + if (cmd is YieldCmd) + return true; + if (cmd is ParCallCmd) + return true; + if (cmd is CallCmd callCmd && yieldingProcs.Contains(callCmd.Proc)) + return true; + } + } + } + + return false; + } + + private void DesugarConcurrency(Implementation impl, Graph graph, HashSet yieldingLoopHeaders) + { + var allYieldPredicates = CollectYields(impl); + var allNonemptyYieldPredicates = allYieldPredicates.Values.Where(x => x.Count > 0); + if (allNonemptyYieldPredicates.Count() > 0) + { + yieldCheckerDecls.AddRange(noninterferenceInstrumentation.CreateYieldCheckerProcImpl(impl, allNonemptyYieldPredicates)); + } + + var yieldCheckerBlock = CreateYieldCheckerBlock(); + var returnBlock = CreateReturnBlock(); + SplitBlocks(impl); + + // add jumps to yieldCheckerBlock and returnBlock + foreach (var b in impl.Blocks) + { + if (b.TransferCmd is GotoCmd gotoCmd) + { + var addEdge = false; + foreach (var nextBlock in gotoCmd.labelTargets) + { + if (nextBlock.cmds.Count > 0) + { + var cmd = nextBlock.cmds[0]; + if (cmd is YieldCmd) + { + addEdge = true; + } + else if (cmd is CallCmd callCmd && yieldingProcs.Contains(callCmd.Proc)) + { + addEdge = true; + } + else if (cmd is ParCallCmd) + { + addEdge = true; + } + } + } + if (addEdge) + { + gotoCmd.labelNames.Add(yieldCheckerBlock.Label); + gotoCmd.labelTargets.Add(yieldCheckerBlock); + } + } + else + { + b.TransferCmd = new GotoCmd(b.TransferCmd.tok, new List {returnBlock.Label}, new List {returnBlock}); + } + } + foreach (Block header in yieldingLoopHeaders) + { + foreach (Block pred in header.Predecessors) + { + var gotoCmd = pred.TransferCmd as GotoCmd; + gotoCmd.labelNames.Add(yieldCheckerBlock.Label); + gotoCmd.labelTargets.Add(yieldCheckerBlock); + } + } + + // desugar YieldCmd, CallCmd, and ParCallCmd + foreach (Block b in impl.Blocks) + { + if (b.cmds.Count > 0) + { + var cmd = b.cmds[0]; + if (cmd is YieldCmd yieldCmd) + { + var newCmds = new List(); + newCmds.AddRange(refinementInstrumentation.CreateUpdateCmds()); + var yieldPredicates = allYieldPredicates[yieldCmd]; + newCmds.AddRange(yieldPredicates); + newCmds.AddRange(DesugarYieldCmd(yieldCmd, yieldPredicates)); + newCmds.AddRange(b.cmds.GetRange(1 + yieldPredicates.Count, b.cmds.Count - (1 + yieldPredicates.Count))); + b.cmds = newCmds; + } + else if (cmd is CallCmd callCmd && yieldingProcs.Contains(callCmd.Proc)) + { + List newCmds = new List(); + newCmds.AddRange(refinementInstrumentation.CreateUpdateCmds()); + if (callCmd.IsAsync) + { + if (!asyncAndParallelCallDesugarings.ContainsKey(callCmd.Proc.Name)) + { + asyncAndParallelCallDesugarings[callCmd.Proc.Name] = new Procedure(Token.NoToken, + $"DummyAsyncTarget_{callCmd.Proc.Name}", + callCmd.Proc.TypeParameters, callCmd.Proc.InParams, callCmd.Proc.OutParams, + callCmd.Proc.Requires, new List(), new List()); + } + + var dummyAsyncTargetProc = asyncAndParallelCallDesugarings[callCmd.Proc.Name]; + CallCmd dummyCallCmd = new CallCmd(callCmd.tok, dummyAsyncTargetProc.Name, callCmd.Ins, + callCmd.Outs, callCmd.Attributes); + dummyCallCmd.Proc = dummyAsyncTargetProc; + newCmds.Add(dummyCallCmd); + } + else + { + newCmds.Add(callCmd); + if (civlTypeChecker.sharedVariables.Count > 0) + { + newCmds.AddRange(refinementInstrumentation.CreateAssumeCmds()); + } + } + newCmds.AddRange(globalSnapshotInstrumentation.CreateUpdatesToOldGlobalVars()); + newCmds.AddRange(refinementInstrumentation.CreateUpdatesToOldOutputVars()); + newCmds.AddRange(noninterferenceInstrumentation.CreateUpdatesToPermissionCollector(callCmd)); + newCmds.AddRange(b.cmds.GetRange(1, b.cmds.Count - 1)); + b.cmds = newCmds; + } + else if (cmd is ParCallCmd parCallCmd) + { + List newCmds = new List(); + newCmds.AddRange(refinementInstrumentation.CreateUpdateCmds()); + newCmds.AddRange(DesugarParCallCmd(parCallCmd)); + if (civlTypeChecker.sharedVariables.Count > 0) + { + newCmds.AddRange(refinementInstrumentation.CreateAssumeCmds()); + } + newCmds.AddRange(globalSnapshotInstrumentation.CreateUpdatesToOldGlobalVars()); + newCmds.AddRange(refinementInstrumentation.CreateUpdatesToOldOutputVars()); + newCmds.AddRange(noninterferenceInstrumentation.CreateUpdatesToPermissionCollector(parCallCmd)); + newCmds.AddRange(b.cmds.GetRange(1, b.cmds.Count - 1)); + b.cmds = newCmds; + } + } + } + + impl.Blocks.AddRange(InstrumentYieldingLoopHeaders(graph, yieldingLoopHeaders)); + impl.Blocks.Add(yieldCheckerBlock); + impl.Blocks.Add(returnBlock); + impl.Blocks.Insert(0, AddInitialBlock(impl)); + } + + private Dictionary> CollectYields(Implementation impl) + { + Dictionary> allYieldPredicates = new Dictionary>(); + List yieldPredicates = new List(); + foreach (Block b in impl.Blocks) + { + YieldCmd yieldCmd = null; + foreach (Cmd cmd in b.Cmds) + { + if (yieldCmd != null) + { + if (cmd is PredicateCmd) + { + yieldPredicates.Add(cmd as PredicateCmd); + } + else + { + allYieldPredicates[yieldCmd] = yieldPredicates; + yieldPredicates = new List(); + yieldCmd = null; + } + } + if (cmd is YieldCmd ycmd) + { + yieldCmd = ycmd; + } + } + if (yieldCmd != null) + { + allYieldPredicates[yieldCmd] = yieldPredicates; + yieldPredicates = new List(); + } + } + return allYieldPredicates; + } + + private void SplitBlocks(Implementation impl) + { + List newBlocks = new List(); + foreach (Block b in impl.Blocks) + { + var currTransferCmd = b.TransferCmd; + int labelCount = 0; + int lastSplitIndex = b.cmds.Count; + for (int i = b.cmds.Count - 1; i >= 0; i--) + { + var split = false; + var cmd = b.cmds[i]; + if (cmd is YieldCmd) + { + split = true; + } + else if (cmd is CallCmd callCmd) + { + if (yieldingProcs.Contains(callCmd.Proc)) + { + split = true; + } + } + else if (cmd is ParCallCmd) + { + split = true; + } + if (split) + { + var newBlock = new Block(b.tok, $"{b.Label}_{labelCount++}", b.cmds.GetRange(i, lastSplitIndex - i), + currTransferCmd); + newBlocks.Add(newBlock); + currTransferCmd = new GotoCmd(b.tok, new List {newBlock.Label}, + new List {newBlock}); + lastSplitIndex = i; + } + } + b.cmds = b.cmds.GetRange(0, lastSplitIndex); + b.TransferCmd = currTransferCmd; + } + impl.Blocks.AddRange(newBlocks); + } + + private Block CreateYieldCheckerBlock() + { + var newCmds = new List(); + newCmds.AddRange(refinementInstrumentation.CreateAssertCmds()); + newCmds.AddRange(noninterferenceInstrumentation.CreateCallToYieldProc()); + newCmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + return new Block(Token.NoToken, "YieldChecker", newCmds, new ReturnCmd(Token.NoToken)); + } + + private Block CreateReturnBlock() + { + var returnBlockCmds = new List(); + returnBlockCmds.AddRange(refinementInstrumentation.CreateUpdateCmds()); + returnBlockCmds.AddRange(refinementInstrumentation.CreateFinalAssertCmds()); + return new Block(Token.NoToken, "ReturnChecker", returnBlockCmds, new ReturnCmd(Token.NoToken)); + } + + private List DesugarYieldCmd( + YieldCmd yieldCmd, + List cmds) + { + var newCmds = new List(); + if (civlTypeChecker.sharedVariableIdentifiers.Count > 0) + { + newCmds.Add(new HavocCmd(Token.NoToken, civlTypeChecker.sharedVariableIdentifiers)); + newCmds.AddRange(refinementInstrumentation.CreateAssumeCmds()); + } + newCmds.AddRange(globalSnapshotInstrumentation.CreateUpdatesToOldGlobalVars()); + newCmds.AddRange(refinementInstrumentation.CreateUpdatesToOldOutputVars()); + newCmds.AddRange(noninterferenceInstrumentation.CreateUpdatesToPermissionCollector(yieldCmd)); + + for (int j = 0; j < cmds.Count; j++) + { + newCmds.Add(new AssumeCmd(Token.NoToken, cmds[j].Expr)); + } + return newCmds; + } + + private List DesugarParCallCmd(ParCallCmd parCallCmd) + { + var newCmds = new List(); + List ins = new List(); + List outs = new List(); + string procName = "og"; + foreach (CallCmd callCmd in parCallCmd.CallCmds) + { + procName = procName + "_" + callCmd.Proc.Name; + ins.AddRange(callCmd.Ins); + outs.AddRange(callCmd.Outs); + } + + if (!asyncAndParallelCallDesugarings.ContainsKey(procName)) + { + List inParams = new List(); + List outParams = new List(); + List requiresSeq = new List(); + List ensuresSeq = new List(); + int count = 0; + foreach (CallCmd callCmd in parCallCmd.CallCmds) + { + Dictionary map = new Dictionary(); + foreach (Variable x in callCmd.Proc.InParams) + { + Variable y = ParCallDesugarFormal(x, count, true); + inParams.Add(y); + map[x] = Expr.Ident(y); + } + foreach (Variable x in callCmd.Proc.OutParams) + { + Variable y = ParCallDesugarFormal(x, count, false); + outParams.Add(y); + map[x] = Expr.Ident(y); + } + Contract.Assume(callCmd.Proc.TypeParameters.Count == 0); + Substitution subst = Substituter.SubstitutionFromHashtable(map); + foreach (Requires req in callCmd.Proc.Requires) + { + requiresSeq.Add(new Requires(req.tok, req.Free, Substituter.Apply(subst, req.Condition), null, + req.Attributes)); + } + foreach (Ensures ens in callCmd.Proc.Ensures) + { + ensuresSeq.Add(new Ensures(ens.tok, ens.Free, Substituter.Apply(subst, ens.Condition), null, + ens.Attributes)); + } + count++; + } + asyncAndParallelCallDesugarings[procName] = new Procedure(Token.NoToken, procName, + new List(), inParams, outParams, requiresSeq, + civlTypeChecker.sharedVariableIdentifiers, ensuresSeq); + } + + Procedure proc = asyncAndParallelCallDesugarings[procName]; + CallCmd dummyCallCmd = new CallCmd(parCallCmd.tok, proc.Name, ins, outs, parCallCmd.Attributes); + dummyCallCmd.Proc = proc; + newCmds.Add(dummyCallCmd); + return newCmds; + } + + private Formal ParCallDesugarFormal(Variable v, int count, bool incoming) + { + return new Formal(Token.NoToken, + new TypedIdent(Token.NoToken, $"og_{count}_{v.Name}", v.TypedIdent.Type), incoming); + } + + private List InstrumentYieldingLoopHeaders(Graph graph, HashSet yieldingLoopHeaders) + { + var newBlocks = new List(); + var headerToInstrumentationBlocks = new Dictionary>(); + var headerToNonBackEdgeInstrumentationBlocks = new Dictionary>(); + foreach (Block header in yieldingLoopHeaders) + { + headerToInstrumentationBlocks[header] = new List(); + headerToNonBackEdgeInstrumentationBlocks[header] = new List(); + foreach (Block pred in header.Predecessors) + { + var gotoCmd = pred.TransferCmd as GotoCmd; + if (gotoCmd.labelTargets.Count == 1) + { + headerToInstrumentationBlocks[header].Add(pred); + if (!graph.BackEdgeNodes(header).Contains(pred)) + { + headerToNonBackEdgeInstrumentationBlocks[header].Add(pred); + } + } + else + { + var newBlock = new Block(Token.NoToken, $"{pred.Label}_to_{header.Label}", new List(), pred.TransferCmd); + pred.TransferCmd = new GotoCmd(Token.NoToken, new List {newBlock.Label}, new List{newBlock}); + headerToInstrumentationBlocks[header].Add(newBlock); + if (!graph.BackEdgeNodes(header).Contains(pred)) + { + headerToNonBackEdgeInstrumentationBlocks[header].Add(newBlock); + } + newBlocks.Add(newBlock); + } + } + } + + foreach (Block header in yieldingLoopHeaders) + { + foreach (Block pred in headerToInstrumentationBlocks[header]) + { + pred.cmds.AddRange(globalSnapshotInstrumentation.CreateUpdatesToOldGlobalVars()); + pred.cmds.AddRange(refinementInstrumentation.CreateUpdatesToOldOutputVars()); + pred.cmds.AddRange(refinementInstrumentation.CreateUpdateCmds()); + pred.cmds.AddRange(noninterferenceInstrumentation.CreateUpdatesToPermissionCollector(header)); + } + + foreach (Block pred in headerToNonBackEdgeInstrumentationBlocks[header]) + { + pred.cmds.AddRange(refinementInstrumentation.CreateYieldingLoopHeaderInitCmds(header)); + } + + var newCmds = new List(); + newCmds.AddRange(globalSnapshotInstrumentation.CreateAssumeCmds()); + newCmds.AddRange(noninterferenceInstrumentation.CreateAssumeCmds(header)); + newCmds.AddRange(refinementInstrumentation.CreateYieldingLoopHeaderAssertCmds(header)); + newCmds.AddRange(header.Cmds); + header.Cmds = newCmds; + } + + return newBlocks; + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/YieldTypeChecker.cs boogie-2.4.1+dfsg/Source/Concurrency/YieldTypeChecker.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Concurrency/YieldTypeChecker.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Concurrency/YieldTypeChecker.cs 2019-10-25 18:17:05.000000000 +0000 @@ -1,368 +1,416 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; -using Microsoft.Boogie; -using System.Diagnostics.Contracts; -using Microsoft.Boogie.AbstractInterpretation; using Microsoft.Boogie.GraphUtil; using System.Diagnostics; +using System.ComponentModel; namespace Microsoft.Boogie { - class YieldTypeChecker + public class YieldTypeChecker { - static List> ASpec; - static List> BSpec; - static List> CSpec; - static YieldTypeChecker() - { - // initial: 0, final: 1 - ASpec = new List>(); - ASpec.Add(new Tuple(0, 'Y', 1)); - ASpec.Add(new Tuple(1, 'Y', 1)); - ASpec.Add(new Tuple(1, 'B', 1)); - ASpec.Add(new Tuple(1, 'R', 1)); - ASpec.Add(new Tuple(1, 'L', 1)); - ASpec.Add(new Tuple(1, 'A', 1)); - ASpec.Add(new Tuple(0, 'P', 0)); - ASpec.Add(new Tuple(1, 'P', 1)); - - // initial: 1, final: 0 - BSpec = new List>(); - BSpec.Add(new Tuple(1, 'Y', 0)); - BSpec.Add(new Tuple(1, 'Y', 1)); - BSpec.Add(new Tuple(1, 'B', 1)); - BSpec.Add(new Tuple(1, 'R', 1)); - BSpec.Add(new Tuple(1, 'L', 1)); - BSpec.Add(new Tuple(1, 'A', 1)); - BSpec.Add(new Tuple(0, 'P', 0)); - BSpec.Add(new Tuple(1, 'P', 1)); - - // initial: {0, 1}, final: {0, 1} - CSpec = new List>(); - CSpec.Add(new Tuple(0, 'B', 0)); - CSpec.Add(new Tuple(0, 'R', 0)); - CSpec.Add(new Tuple(0, 'Y', 0)); - CSpec.Add(new Tuple(0, 'B', 1)); - CSpec.Add(new Tuple(0, 'R', 1)); - CSpec.Add(new Tuple(0, 'L', 1)); - CSpec.Add(new Tuple(0, 'A', 1)); - CSpec.Add(new Tuple(1, 'B', 1)); - CSpec.Add(new Tuple(1, 'L', 1)); - CSpec.Add(new Tuple(1, 'Y', 0)); - CSpec.Add(new Tuple(0, 'P', 0)); - CSpec.Add(new Tuple(1, 'P', 1)); - } + // Edge labels of the automaton that abstracts the code of a procedure + private const char Y = 'Y'; // yield + private const char B = 'B'; // both mover action + private const char L = 'L'; // left mover action + private const char R = 'R'; // right mover action + private const char A = 'A'; // atomic (non mover) action + private const char P = 'P'; // private (local variable) access + private const char I = 'I'; // introduction action + + // States of Bracket Automaton (check that all accesses to global variables are bracketed by yields) + private const int BEFORE = 0; + private const int INSIDE = 1; + private const int AFTER = 2; + + // Transitions of Bracket Automaton + static List> BracketSpec = new List> + { // initial: BEFORE, final: AFTER + new Tuple(BEFORE, P, BEFORE), + new Tuple(BEFORE, Y, INSIDE), + new Tuple(BEFORE, Y, AFTER), + new Tuple(INSIDE, Y, INSIDE), + new Tuple(INSIDE, B, INSIDE), + new Tuple(INSIDE, R, INSIDE), + new Tuple(INSIDE, L, INSIDE), + new Tuple(INSIDE, A, INSIDE), + new Tuple(INSIDE, P, INSIDE), + new Tuple(INSIDE, I, INSIDE), + new Tuple(INSIDE, Y, AFTER), + new Tuple(AFTER, P, AFTER), + }; + + // States of Atomicity Automaton (check that transactions are separated by yields) + private const int RM = 0; + private const int LM = 1; + + // Transitions of Atomicity Automaton + static List> AtomicitySpec = new List> + { // initial: {RM, LM}, final: {RM, LM} + new Tuple(RM, P, RM), + new Tuple(RM, I, RM), + new Tuple(RM, B, RM), + new Tuple(RM, R, RM), + new Tuple(RM, Y, RM), + new Tuple(RM, L, LM), + new Tuple(RM, A, LM), + new Tuple(LM, P, LM), + new Tuple(LM, I, LM), + new Tuple(LM, B, LM), + new Tuple(LM, L, LM), + new Tuple(LM, Y, RM), + }; - private void IsYieldTypeSafe() + private static int MoverTypeToLabel(MoverType moverType) { - List> implEdges = new List>(); - foreach (Tuple e in edgeLabels.Keys) + switch (moverType) { - implEdges.Add(new Tuple(e.Item1, edgeLabels[e], e.Item2)); + case MoverType.Atomic: + return A; + case MoverType.Both: + return B; + case MoverType.Left: + return L; + case MoverType.Right: + return R; + default: + throw new InvalidEnumArgumentException(); } - //Console.WriteLine(PrintGraph(impl, implEdges, initialState, finalStates)); - ASpecCheck(implEdges); - BSpecCheck(implEdges); - CSpecCheck(implEdges); } - private void ASpecCheck(List> implEdges) + CivlTypeChecker civlTypeChecker; + public CheckingContext checkingContext; + private Graph moverProcedureCallGraph; + + public YieldTypeChecker(CivlTypeChecker civlTypeChecker) { - Dictionary> initialConstraints = new Dictionary>(); - initialConstraints[initialState] = new HashSet(new int[] { 0 }); - foreach (var finalState in finalStates) - { - initialConstraints[finalState] = new HashSet(new int[] { 1 }); - } - SimulationRelation x = new SimulationRelation(implEdges, ASpec, initialConstraints); - Dictionary> simulationRelation = x.ComputeSimulationRelation(); - if (simulationRelation[initialState].Count == 0) - { - civlTypeChecker.Error(impl, string.Format("Implementation {0} fails simulation check A at layer {1}. An action must be preceded by a yield.\n", impl.Name, currLayerNum)); - } + this.civlTypeChecker = civlTypeChecker; + this.checkingContext = new CheckingContext(null); + this.moverProcedureCallGraph = new Graph(); } - private void BSpecCheck(List> implEdges) + public void TypeCheck() { - Dictionary> initialConstraints = new Dictionary>(); - initialConstraints[initialState] = new HashSet(new int[] { 1 }); - foreach (var finalState in finalStates) + // Mover procedures can only call other mover procedures on the same layer. + // Thus, the constructed call graph naturally forms disconnected components w.r.t. layers and we + // can keep a single graph instead of one for each layer; + foreach (var impl in civlTypeChecker.program.Implementations.Where(impl => civlTypeChecker.procToYieldingProc.ContainsKey(impl.Proc))) { - initialConstraints[finalState] = new HashSet(new int[] { 0 }); + MoverProc callerProc = civlTypeChecker.procToYieldingProc[impl.Proc] as MoverProc; + if (callerProc == null) continue; + + foreach (var callCmd in impl.Blocks.SelectMany(b => b.Cmds).OfType()) + { + if (civlTypeChecker.procToYieldingProc.ContainsKey(callCmd.Proc)) + { + MoverProc calleeProc = civlTypeChecker.procToYieldingProc[callCmd.Proc] as MoverProc; + if (calleeProc == null) continue; + + Debug.Assert(callerProc.upperLayer == calleeProc.upperLayer); + moverProcedureCallGraph.AddEdge(callerProc, calleeProc); + } + } } - SimulationRelation x = new SimulationRelation(implEdges, BSpec, initialConstraints); - Dictionary> simulationRelation = x.ComputeSimulationRelation(); - if (simulationRelation[initialState].Count == 0) + + foreach (var impl in civlTypeChecker.program.Implementations.Where(impl => civlTypeChecker.procToYieldingProc.ContainsKey(impl.Proc))) { - civlTypeChecker.Error(impl, string.Format("Implementation {0} fails simulation check B at layer {1}. An action must be succeeded by a yield.\n", impl.Name, currLayerNum)); + var yieldingProc = civlTypeChecker.procToYieldingProc[impl.Proc]; + + impl.PruneUnreachableBlocks(); + Graph implGraph = Program.GraphFromImpl(impl); + implGraph.ComputeLoops(); + + foreach (int layerNum in civlTypeChecker.allRefinementLayers.Where(l => l <= yieldingProc.upperLayer)) + { + PerLayerYieldTypeChecker perLayerTypeChecker = new PerLayerYieldTypeChecker(this, yieldingProc, impl, layerNum, implGraph); + perLayerTypeChecker.TypeCheckLayer(); + } } + + // To allow garbage collection + moverProcedureCallGraph = null; + + // TODO: Remove "terminates" attribute } - private void CSpecCheck(List> implEdges) + private class PerLayerYieldTypeChecker { - Dictionary> initialConstraints = new Dictionary>(); - foreach (Block block in loopHeaders) + YieldTypeChecker @base; + YieldingProc yieldingProc; + Implementation impl; + int currLayerNum; + Graph implGraph; + + List> implEdges; + Absy initialState; + HashSet finalStates; + + public PerLayerYieldTypeChecker(YieldTypeChecker @base, YieldingProc yieldingProc, Implementation impl, int currLayerNum, Graph implGraph) + { + this.@base = @base; + this.yieldingProc = yieldingProc; + this.impl = impl; + this.currLayerNum = currLayerNum; + this.implGraph = implGraph; + this.initialState = impl.Blocks[0]; + this.finalStates = new HashSet(); + this.implEdges = new List>(); + } + + public void TypeCheckLayer() { - if (!IsTerminatingLoopHeader(block)) + ComputeGraph(); + // Console.WriteLine(PrintGraph(impl, implEdges, initialState, finalStates)); + + if (!IsMoverProcedure) { - initialConstraints[absyToNode[block]] = new HashSet(new int[] { 0 }); + BracketCheck(); } + AtomicityCheck(); } - SimulationRelation x = new SimulationRelation(implEdges, CSpec, initialConstraints); - Dictionary> simulationRelation = x.ComputeSimulationRelation(); - if (simulationRelation[initialState].Count == 0) - { - civlTypeChecker.Error(impl, string.Format("Implementation {0} fails simulation check C at layer {1}. Transactions must be separated by a yield.\n", impl.Name, currLayerNum)); - } - } - private bool IsTerminatingLoopHeader(Block block) - { - foreach (Cmd cmd in block.Cmds) + private void BracketCheck() { - AssertCmd assertCmd = cmd as AssertCmd; - if (assertCmd != null && QKeyValue.FindBoolAttribute(assertCmd.Attributes, "terminates") && civlTypeChecker.absyToLayerNums[assertCmd].Contains(currLayerNum)) + var initialConstraints = new Dictionary>(); + initialConstraints[initialState] = new HashSet { BEFORE }; + foreach (var finalState in finalStates) { - return true; + initialConstraints[finalState] = new HashSet { AFTER }; + } + + var simulationRelation = new SimulationRelation(implEdges, BracketSpec, initialConstraints).ComputeSimulationRelation(); + if (simulationRelation[initialState].Count == 0) + { + @base.checkingContext.Error(impl, $"Implementation {impl.Name} fails bracket check at layer {currLayerNum}. All code that accesses global variables must be bracketed by yields."); } } - return false; - } - - public static void PerformYieldSafeCheck(CivlTypeChecker civlTypeChecker) - { - foreach (var impl in civlTypeChecker.program.Implementations) + + private void AtomicityCheck() { - if (!civlTypeChecker.procToActionInfo.ContainsKey(impl.Proc)) continue; - impl.PruneUnreachableBlocks(); - Graph implGraph = Program.GraphFromImpl(impl); - implGraph.ComputeLoops(); - int specLayerNum = civlTypeChecker.procToActionInfo[impl.Proc].createdAtLayerNum; - foreach (int layerNum in civlTypeChecker.AllLayerNums) + var initialConstraints = new Dictionary>(); + + foreach (Block header in implGraph.Headers) { - if (layerNum > specLayerNum) continue; - YieldTypeChecker executor = new YieldTypeChecker(civlTypeChecker, impl, layerNum, implGraph.Headers); + if (!IsTerminatingLoopHeader(header)) + { + initialConstraints[header] = new HashSet { RM }; + } + } + if (IsMoverProcedure) + { + foreach (var call in impl.Blocks.SelectMany(b => b.cmds).OfType()) + { + if (!IsTerminatingCall(call)) + { + initialConstraints[call] = new HashSet { RM }; + } + } } - } - } - int stateCounter; - CivlTypeChecker civlTypeChecker; - Implementation impl; - int currLayerNum; - Dictionary absyToNode; - Dictionary nodeToAbsy; - int initialState; - HashSet finalStates; - Dictionary, int> edgeLabels; - IEnumerable loopHeaders; + var simulationRelation = new SimulationRelation(implEdges, AtomicitySpec, initialConstraints).ComputeSimulationRelation(); - private YieldTypeChecker(CivlTypeChecker civlTypeChecker, Implementation impl, int currLayerNum, IEnumerable loopHeaders) - { - this.civlTypeChecker = civlTypeChecker; - this.impl = impl; - this.currLayerNum = currLayerNum; - this.loopHeaders = loopHeaders; - this.stateCounter = 0; - this.absyToNode = new Dictionary(); - this.initialState = 0; - this.finalStates = new HashSet(); - this.edgeLabels = new Dictionary, int>(); - - foreach (Block block in impl.Blocks) - { - absyToNode[block] = stateCounter; - stateCounter++; - foreach (Cmd cmd in block.Cmds) + if (IsMoverProcedure) { - absyToNode[cmd] = stateCounter; - stateCounter++; + if (!CheckAtomicity(simulationRelation)) + { + @base.checkingContext.Error(impl, "The atomicity declared for mover procedure is not valid"); + } } - absyToNode[block.TransferCmd] = stateCounter; - stateCounter++; - if (block.TransferCmd is ReturnCmd) + else if (simulationRelation[initialState].Count == 0) { - finalStates.Add(absyToNode[block.TransferCmd]); + @base.checkingContext.Error(impl, $"Implementation {impl.Name} fails atomicity check at layer {currLayerNum}. Transactions must be separated by yields."); } } - foreach (Block block in impl.Blocks) + + private bool IsMoverProcedure { get { return yieldingProc is MoverProc && yieldingProc.upperLayer == currLayerNum; } } + + private bool IsTerminatingLoopHeader(Block block) { - Absy blockEntry = block.Cmds.Count == 0 ? (Absy)block.TransferCmd : (Absy)block.Cmds[0]; - edgeLabels[new Tuple(absyToNode[block], absyToNode[blockEntry])] = 'P'; - - GotoCmd gotoCmd = block.TransferCmd as GotoCmd; - if (gotoCmd == null) continue; - foreach (Block successor in gotoCmd.labelTargets) - { - edgeLabels[new Tuple(absyToNode[gotoCmd], absyToNode[successor])] = 'P'; - } + return block.cmds.OfType().Any(c => c.HasAttribute(CivlAttributes.TERMINATES) && @base.civlTypeChecker.absyToLayerNums[c].Contains(currLayerNum)); } - this.nodeToAbsy = new Dictionary(); - foreach (KeyValuePair state in absyToNode) + private bool IsTerminatingCall(CallCmd call){ + return !IsRecursiveMoverProcedureCall(call) || call.Proc.HasAttribute(CivlAttributes.TERMINATES); + } + + private bool CheckAtomicity(Dictionary> simulationRelation) { - this.nodeToAbsy[state.Value] = state.Key; + if (yieldingProc.moverType == MoverType.Atomic && simulationRelation[initialState].Count == 0) return false; + if (yieldingProc.IsRightMover && (!simulationRelation[initialState].Contains(RM) || finalStates.Any(f => !simulationRelation[f].Contains(RM)))) return false; + if (yieldingProc.IsLeftMover && !simulationRelation[initialState].Contains(LM)) return false; + return true; } - ComputeGraph(); - IsYieldTypeSafe(); - } + private bool IsRecursiveMoverProcedureCall(CallCmd call) + { + MoverProc source = null; + if (@base.civlTypeChecker.procToYieldingProc.ContainsKey(call.Proc)) + source = @base.civlTypeChecker.procToYieldingProc[call.Proc] as MoverProc; + if (source == null) + return false; - private void ComputeGraph() - { - foreach (Block block in impl.Blocks) + MoverProc target = (MoverProc)yieldingProc; + + HashSet frontier = new HashSet { source }; + HashSet visited = new HashSet(); + + while (frontier.Count > 0) + { + var curr = frontier.First(); + frontier.Remove(curr); + visited.Add(curr); + + if (curr == target) + return true; + frontier.UnionWith(@base.moverProcedureCallGraph.Successors(curr).Except(visited)); + } + + return false; + } + + private void ComputeGraph() { - for (int i = 0; i < block.Cmds.Count; i++) + // Internal representation + // At the end of the method, we translate to List> + Dictionary, int> edgeLabels = new Dictionary, int>(); + + foreach (Block block in impl.Blocks) { - Cmd cmd = block.Cmds[i]; - int curr = absyToNode[cmd]; - int next = (i + 1 == block.Cmds.Count) ? absyToNode[block.TransferCmd] : absyToNode[block.Cmds[i + 1]]; - Tuple edge = new Tuple(curr, next); - if (cmd is CallCmd) + // Block entry edge + Absy blockEntry = block.Cmds.Count == 0 ? (Absy)block.TransferCmd : (Absy)block.Cmds[0]; + edgeLabels[new Tuple(block, blockEntry)] = P; + + // Block exit edges + if (block.TransferCmd is GotoCmd gotoCmd) { - CallCmd callCmd = cmd as CallCmd; - if (callCmd.IsAsync) - { - ActionInfo actionInfo = civlTypeChecker.procToActionInfo[callCmd.Proc]; - if (currLayerNum <= actionInfo.createdAtLayerNum) - edgeLabels[edge] = 'L'; - else - edgeLabels[edge] = 'B'; - } - else if (!civlTypeChecker.procToActionInfo.ContainsKey(callCmd.Proc)) + foreach (Block successor in gotoCmd.labelTargets) { - edgeLabels[edge] = 'P'; - } - else - { - MoverType moverType; - ActionInfo actionInfo = civlTypeChecker.procToActionInfo[callCmd.Proc]; - if (actionInfo.createdAtLayerNum >= currLayerNum) - { - moverType = MoverType.Top; - } - else - { - AtomicActionInfo atomicActionInfo = actionInfo as AtomicActionInfo; - if (atomicActionInfo == null) - moverType = MoverType.Both; - else - moverType = atomicActionInfo.moverType; - } - switch (moverType) - { - case MoverType.Atomic: - edgeLabels[edge] = 'A'; - break; - case MoverType.Both: - edgeLabels[edge] = 'B'; - break; - case MoverType.Left: - edgeLabels[edge] = 'L'; - break; - case MoverType.Right: - edgeLabels[edge] = 'R'; - break; - case MoverType.Top: - edgeLabels[edge] = 'Y'; - break; - } + edgeLabels[new Tuple(block.TransferCmd, successor)] = P; } } - else if (cmd is ParCallCmd) + else if (block.TransferCmd is ReturnCmd) { - ParCallCmd parCallCmd = cmd as ParCallCmd; - bool isYield = false; - bool isRightMover = true; - bool isLeftMover = true; - foreach (CallCmd callCmd in parCallCmd.CallCmds) + finalStates.Add(block.TransferCmd); + } + + // Block internal edges + for (int i = 0; i < block.Cmds.Count; i++) + { + Cmd cmd = block.Cmds[i]; + Absy next = (i + 1 == block.Cmds.Count) ? (Absy)block.TransferCmd : block.Cmds[i + 1]; + Tuple edge = new Tuple(cmd, next); + if (cmd is CallCmd callCmd) + { + edgeLabels[edge] = CallCmdLabel(callCmd); + } + else if (cmd is ParCallCmd parCallCmd) { - if (civlTypeChecker.procToActionInfo[callCmd.Proc].createdAtLayerNum >= currLayerNum) - { - isYield = true; - } + edgeLabels[edge] = ParCallCmdLabel(parCallCmd); } - if (isYield) + else if (cmd is YieldCmd) { - edgeLabels[edge] = 'Y'; + edgeLabels[edge] = Y; } else { - int numAtomicActions = 0; - foreach (CallCmd callCmd in parCallCmd.CallCmds) - { - ActionInfo actionInfo = civlTypeChecker.procToActionInfo[callCmd.Proc]; - isRightMover = isRightMover && actionInfo.IsRightMover; - isLeftMover = isLeftMover && actionInfo.IsLeftMover; - if (actionInfo is AtomicActionInfo) - { - numAtomicActions++; - } - } - if (isLeftMover && isRightMover) - { - edgeLabels[edge] = 'B'; - } - else if (isLeftMover) - { - edgeLabels[edge] = 'L'; - } - else if (isRightMover) - { - edgeLabels[edge] = 'R'; - } - else - { - Debug.Assert(numAtomicActions == 1); - edgeLabels[edge] = 'A'; - } + edgeLabels[edge] = P; } } - else if (cmd is YieldCmd) + } + + foreach (Tuple e in edgeLabels.Keys) + { + implEdges.Add(new Tuple(e.Item1, edgeLabels[e], e.Item2)); + } + } + + private int CallCmdLabel(CallCmd callCmd) + { + if (@base.civlTypeChecker.procToIntroductionProc.ContainsKey(callCmd.Proc)) + return I; + + YieldingProc callee = @base.civlTypeChecker.procToYieldingProc[callCmd.Proc]; + if (callCmd.IsAsync) + { + if ((currLayerNum < yieldingProc.upperLayer && currLayerNum > callee.upperLayer) || + (currLayerNum == yieldingProc.upperLayer && callee.upperLayer < yieldingProc.upperLayer)) { - edgeLabels[edge] = 'Y'; + return MoverTypeToLabel(callee.moverType); } - else + return L; + } + else + { + if (callee.upperLayer < currLayerNum || (callee.upperLayer == currLayerNum && callee is MoverProc)) { - edgeLabels[edge] = 'P'; + return MoverTypeToLabel(callee.moverType); } + return Y; } } - } - private static string PrintGraph(Implementation impl, List> edges, int initialState, HashSet finalStates) - { - var s = new StringBuilder(); - s.AppendLine("\nImplementation " + impl.Proc.Name + " digraph G {"); - foreach (var e in edges) - { - string label = "P"; - switch (e.Item2) - { - case 'P': label = "P"; break; - case 'Y': label = "Y"; break; - case 'B': label = "B"; break; - case 'R': label = "R"; break; - case 'L': label = "L"; break; - case 'A': label = "A"; break; - default: Debug.Assert(false); break; - } - s.AppendLine(" \"" + e.Item1.ToString() + "\" -- " + label + " --> " + " \"" + e.Item3.ToString() + "\";"); - } - s.AppendLine("}"); - s.AppendLine("Initial state: " + initialState); - s.Append("Final states: "); - bool first = true; - foreach (int finalState in finalStates) - { - s.Append((first ? "" : ", ") + finalState); - first = false; + private int ParCallCmdLabel(ParCallCmd parCallCmd) + { + foreach (CallCmd callCmd in parCallCmd.CallCmds) + { + if (@base.civlTypeChecker.procToYieldingProc[callCmd.Proc].upperLayer >= currLayerNum) + return Y; + } + + bool isRightMover = true; + bool isLeftMover = true; + int numAtomicActions = 0; + foreach (CallCmd callCmd in parCallCmd.CallCmds) + { + YieldingProc callee = @base.civlTypeChecker.procToYieldingProc[callCmd.Proc]; + isRightMover = isRightMover && callee.IsRightMover; + isLeftMover = isLeftMover && callee.IsLeftMover; + if (callee is ActionProc) + { + numAtomicActions++; + } + } + + if (isLeftMover && isRightMover) + return B; + else if (isLeftMover) + return L; + else if (isRightMover) + return R; + + Debug.Assert(numAtomicActions == 1); + return A; + } + + private static string PrintGraph(Implementation impl, List> edges, Absy initialState, HashSet finalStates) + { + Dictionary map = new Dictionary(); + int cnt = 0; + foreach (var e in edges) + { + if (!map.ContainsKey(e.Item1)) map[e.Item1] = cnt++; + if (!map.ContainsKey(e.Item3)) map[e.Item3] = cnt++; + } + + var s = new StringBuilder(); + s.AppendLine("\nImplementation " + impl.Proc.Name + " digraph G {"); + foreach (var e in edges) + { + s.AppendLine(" \"" + map[e.Item1] + "\" -- " + (char)e.Item2 + " --> \"" + map[e.Item3] + "\";"); + } + s.AppendLine("}"); + s.AppendLine("Initial state: " + map[initialState]); + s.Append("Final states: "); + bool first = true; + foreach (var finalState in finalStates) + { + s.Append((first ? "" : ", ") + map[finalState]); + first = false; + } + s.AppendLine(); + return s.ToString(); } - s.AppendLine(); - return s.ToString(); } } } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/AbsyCmd.cs boogie-2.4.1+dfsg/Source/Core/AbsyCmd.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/AbsyCmd.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/AbsyCmd.cs 2019-10-25 18:17:05.000000000 +0000 @@ -1272,14 +1272,7 @@ } else if (!CommandLineOptions.Clo.DoModSetAnalysis && v is GlobalVariable) { - if (tc.Yields) { - // a yielding procedure is allowed to modify any global variable - } - else if (tc.Frame == null) - { - tc.Error(this, "update to a global variable allowed only inside an atomic action of a yielding procedure"); - } - else if (!tc.InFrame(v)) + if (!tc.Yields && !tc.InFrame(v)) { tc.Error(this, "command assigns to a global variable that is not in the enclosing procedure's modifies clause: {0}", v.Name); } @@ -1467,7 +1460,9 @@ // class for parallel assignments, which subsumes both the old // SimpleAssignCmd and the old MapAssignCmd - public class AssignCmd : Cmd { + public class AssignCmd : Cmd, ICarriesAttributes { + public QKeyValue Attributes { get; set; } + private List/*!*/ _lhss; public IList/*!*/ Lhss { @@ -1519,13 +1514,25 @@ } - public AssignCmd(IToken tok, IList/*!*/ lhss, IList/*!*/ rhss) + public AssignCmd(IToken tok, IList/*!*/ lhss, + IList/*!*/ rhss, QKeyValue kv) : base(tok) { Contract.Requires(tok != null); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(cce.NonNullElements(lhss)); this._lhss = new List(lhss); this._rhss = new List(rhss); + this.Attributes = kv; + } + + public AssignCmd(IToken tok, IList/*!*/ lhss, IList/*!*/ rhss) + : base(tok) + { + Contract.Requires(tok != null); + Contract.Requires(cce.NonNullElements(rhss)); + Contract.Requires(cce.NonNullElements(lhss)); + this._lhss = new List(lhss); + this._rhss = new List(rhss); } public override void Emit(TokenTextWriter stream, int level) { @@ -1658,7 +1665,7 @@ newRhss.Add(newRhs); } - return new AssignCmd(Token.NoToken, newLhss, newRhss); + return new AssignCmd(Token.NoToken, newLhss, newRhss, Attributes); } } @@ -2108,8 +2115,8 @@ } } - public abstract class CallCommonality : SugaredCmd { - public QKeyValue Attributes; + public abstract class CallCommonality : SugaredCmd, ICarriesAttributes { + public QKeyValue Attributes { get; set; } private bool isFree = false; public bool IsFree { @@ -2247,7 +2254,7 @@ } foreach (CallCmd callCmd in CallCmds) { - if (!QKeyValue.FindBoolAttribute(callCmd.Proc.Attributes, "yields")) + if (!QKeyValue.FindBoolAttribute(callCmd.Proc.Attributes, CivlAttributes.YIELDS)) { tc.Error(callCmd, "target procedure of a parallel call must yield"); } @@ -2430,7 +2437,7 @@ } if (IsAsync) { if (Proc.OutParams.Count > 0) { - rc.Error(this.tok, "a procedure called asynchronously can have no output parameters"); + rc.Error(this.tok, "a procedure called asynchronously cannot have output parameters"); return; } } @@ -2539,14 +2546,20 @@ Contract.Assert(cce.NonNullElements(actualTypeParams)); TypeParameters = SimpleTypeParamInstantiation.From(Proc.TypeParameters, actualTypeParams); - + if (!CommandLineOptions.Clo.DoModSetAnalysis && IsAsync) { + if (!tc.Yields) { - tc.Error(this, "enclosing procedure of an async call must yield"); + // TODO: Fix this (and related) checks. + // We now allow pending asyncs in atomic actions. + // Maybe we should not support "yields inference" ala modset analysis anymore. + // In particular, we need to check if it would even make sense with the new design of CIVL. + + // tc.Error(this, "enclosing procedure of an async call must yield"); } - if (!QKeyValue.FindBoolAttribute(Proc.Attributes, "yields")) + if (!QKeyValue.FindBoolAttribute(Proc.Attributes, CivlAttributes.YIELDS)) { tc.Error(this, "target procedure of an async call must yield"); } @@ -2872,8 +2885,8 @@ } } - public abstract class PredicateCmd : Cmd { - public QKeyValue Attributes; + public abstract class PredicateCmd : Cmd, ICarriesAttributes { + public QKeyValue Attributes { get; set; } public /*readonly--except in StandardVisitor*/ Expr/*!*/ Expr; [ContractInvariantMethod] void ObjectInvariant() { diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Absy.cs boogie-2.4.1+dfsg/Source/Core/Absy.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Absy.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/Absy.cs 2019-10-25 18:17:05.000000000 +0000 @@ -293,6 +293,11 @@ #endregion } + + public interface ICarriesAttributes + { + QKeyValue Attributes { get; set; } + } [ContractClassFor(typeof(Absy))] public abstract class AbsyContracts : Absy { @@ -344,7 +349,7 @@ this.topLevelDeclarations.Emit(stream); } - public void ProcessDatatypeConstructors() { + public void ProcessDatatypeConstructors(Errors errors) { Dictionary constructors = new Dictionary(); List prunedTopLevelDeclarations = new List(); foreach (Declaration decl in TopLevelDeclarations) { @@ -353,14 +358,22 @@ prunedTopLevelDeclarations.Add(decl); continue; } - if (constructors.ContainsKey(func.Name)) continue; + if (constructors.ContainsKey(func.Name)) + { + errors.SemErr(func.tok, string.Format("more than one declaration of datatype constructor name: {0}", func.Name)); + continue; + } DatatypeConstructor constructor = new DatatypeConstructor(func); constructors.Add(func.Name, constructor); prunedTopLevelDeclarations.Add(constructor); } + if (errors.count > 0) + { + return; + } + ClearTopLevelDeclarations(); AddTopLevelDeclarations(prunedTopLevelDeclarations); - foreach (DatatypeConstructor f in constructors.Values) { for (int i = 0; i < f.InParams.Count; i++) { DatatypeSelector selector = new DatatypeSelector(f, i); @@ -1362,8 +1375,8 @@ // Declarations [ContractClass(typeof(DeclarationContracts))] - public abstract class Declaration : Absy { - public QKeyValue Attributes; + public abstract class Declaration : Absy, ICarriesAttributes { + public QKeyValue Attributes { get; set; } public Declaration(IToken tok) : base(tok) { @@ -1641,6 +1654,16 @@ } } + public int ResourceLimit + { + get + { + int rl = CommandLineOptions.Clo.Resourcelimit; + CheckIntAttribute("rlimit", ref rl); + return rl; + } + } + public NamedDeclaration(IToken/*!*/ tok, string/*!*/ name) : base(tok) { Contract.Requires(tok != null); @@ -1932,7 +1955,7 @@ EmitVitals(stream, level, true); stream.WriteLine(";"); } - public void EmitVitals(TokenTextWriter stream, int level, bool emitAttributes) { + public void EmitVitals(TokenTextWriter stream, int level, bool emitAttributes, bool emitType = true) { Contract.Requires(stream != null); if (emitAttributes) { EmitAttributes(stream); @@ -1940,7 +1963,7 @@ if (CommandLineOptions.Clo.PrintWithUniqueASTIds && this.TypedIdent.HasName) { stream.Write("h{0}^^", this.GetHashCode()); // the idea is that this will prepend the name printed by TypedIdent.Emit } - this.TypedIdent.Emit(stream); + this.TypedIdent.Emit(stream, emitType); } public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); @@ -2855,7 +2878,7 @@ : base(tok, name, args, result) { } } - public class Requires : Absy, IPotentialErrorNode { + public class Requires : Absy, ICarriesAttributes, IPotentialErrorNode { public readonly bool Free; private Expr/*!*/ _condition; @@ -2900,7 +2923,7 @@ } } - public QKeyValue Attributes; + public QKeyValue Attributes { get; set; } public String ErrorMessage { get { @@ -2967,7 +2990,7 @@ } } - public class Ensures : Absy, IPotentialErrorNode { + public class Ensures : Absy, ICarriesAttributes, IPotentialErrorNode { public readonly bool Free; private Expr/*!*/ _condition; @@ -3017,7 +3040,7 @@ } } - public QKeyValue Attributes; + public QKeyValue Attributes { get; set; } public Ensures(IToken token, bool free, Expr/*!*/ condition, string comment, QKeyValue kv) : base(token) { @@ -3229,7 +3252,7 @@ e.Typecheck(tc); } bool oldYields = tc.Yields; - tc.Yields = QKeyValue.FindBoolAttribute(Attributes, "yields"); + tc.Yields = QKeyValue.FindBoolAttribute(Attributes, CivlAttributes.YIELDS); foreach (Ensures/*!*/ e in Ensures) { Contract.Assert(e != null); e.Typecheck(tc); @@ -3399,7 +3422,7 @@ Expr inl = this.FindExprAttribute("inline"); if (inl == null) inl = this.Proc.FindExprAttribute("inline"); - if (inl != null && inl is LiteralExpr && ((LiteralExpr)inl).isBigNum && ((LiteralExpr)inl).asBigNum.Signum > 0) { + if (inl != null) { return true; } } @@ -3764,7 +3787,7 @@ List oldFrame = tc.Frame; bool oldYields = tc.Yields; tc.Frame = Proc.Modifies; - tc.Yields = QKeyValue.FindBoolAttribute(Proc.Attributes, "yields"); + tc.Yields = QKeyValue.FindBoolAttribute(Proc.Attributes, CivlAttributes.YIELDS); foreach (Block b in Blocks) { b.Typecheck(tc); } @@ -4145,14 +4168,21 @@ return this.Name != NoName; } } - public void Emit(TokenTextWriter stream) { + /// + /// An "emitType" value of "false" is ignored if "this.Name" is "NoName". + /// + public void Emit(TokenTextWriter stream, bool emitType) { Contract.Requires(stream != null); stream.SetToken(this); stream.push(); - if (this.Name != NoName) { + if (this.Name != NoName && emitType) { stream.Write("{0}: ", TokenTextWriter.SanitizeIdentifier(this.Name)); + this.Type.Emit(stream); + } else if (this.Name != NoName) { + stream.Write("{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); + } else { + this.Type.Emit(stream); } - this.Type.Emit(stream); if (this.WhereExpr != null) { stream.sep(); stream.Write(" where "); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/AbsyExpr.cs boogie-2.4.1+dfsg/Source/Core/AbsyExpr.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/AbsyExpr.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/AbsyExpr.cs 2019-10-25 18:17:05.000000000 +0000 @@ -368,6 +368,11 @@ Contract.Ensures(Contract.Result() != null); return new LiteralExpr(Token.NoToken, value); } + public static LiteralExpr Literal(RoundingMode value) + { + Contract.Ensures(Contract.Result() != null); + return new LiteralExpr(Token.NoToken, value); + } private static LiteralExpr/*!*/ true_ = Literal(true); public static LiteralExpr/*!*/ True { @@ -502,6 +507,29 @@ } return result; } + + public static Expr Or(IEnumerable disjuncts, bool returnNullIfEmpty = false) + { + Expr result = null; + foreach (var d in disjuncts) + { + if (result != null) + { + result = LiteralExpr.Or(result, d); + result.Type = Type.Bool; + } + else + { + result = d; + result.Type = Type.Bool; + } + } + if (result == null && !returnNullIfEmpty) + { + result = Expr.False; + } + return result; + } } [ContractClassFor(typeof(Expr))] public abstract class ExprContracts : Expr { @@ -526,7 +554,7 @@ } public class LiteralExpr : Expr { - public readonly object/*!*/ Val; // false, true, a BigNum, a BigDec, a BigFloat, or a BvConst + public readonly object/*!*/ Val; // false, true, a BigNum, a BigDec, a BigFloat, a BvConst, or a RoundingMode [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Val != null); @@ -602,6 +630,21 @@ CachedHashCode = ComputeHashCode(); } + /// + /// Creates a literal expression for the rounding mode value "v". + /// + /// + /// + public LiteralExpr(IToken/*!*/ tok, RoundingMode v, bool immutable = false) + : base(tok, immutable) + { + Contract.Requires(tok != null); + Val = v; + Type = Type.RMode; + if (immutable) + CachedHashCode = ComputeHashCode(); + } + [Pure] [Reads(ReadsAttribute.Reads.Nothing)] public override bool Equals(object obj) { @@ -665,6 +708,8 @@ return Type.GetFloatType(temp.SignificandSize, temp.ExponentSize); } else if (Val is BvConst) { return Type.GetBvType(((BvConst)Val).Bits); + } else if (Val is RoundingMode) { + return Type.RMode; } else { { Contract.Assert(false); @@ -759,6 +804,23 @@ } } + public bool isRoundingMode + { + get + { + return Val is RoundingMode; + } + } + + public RoundingMode asRoundingMode + { + get + { + Contract.Assert(isRoundingMode); + return (RoundingMode)cce.NonNull(Val); + } + } + public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); Contract.Ensures(Contract.Result() != null); @@ -849,13 +911,9 @@ /// - /// Creates an unresolved identifier expression. This constructor is intended to be called - /// only from within the parser; for use inside the translation, use another constructor, which - /// specifies the type of the expression. + /// Creates an unresolved identifier expression. /// - /// - /// - internal IdentifierExpr(IToken/*!*/ tok, string/*!*/ name, bool immutable=false) + public IdentifierExpr(IToken/*!*/ tok, string/*!*/ name, bool immutable=false) : base(tok, immutable) { Contract.Requires(tok != null); Contract.Requires(name != null); @@ -1723,11 +1781,11 @@ if (arg0type.Unify(Type.Real) && arg1type.Unify(Type.Real)) { return Type.Real; } - if (arg0type.IsFloat && arg0type.Unify(arg1type)) { - return Type.GetFloatType(arg0.Type.FloatSignificand, arg0.Type.FloatExponent); - } - if (arg1type.IsFloat && arg1type.Unify(arg0type)) { - return Type.GetFloatType(arg1.Type.FloatSignificand, arg1.Type.FloatExponent); + if (arg0type.IsFloat && arg1type.IsFloat) { + if (arg0type.FloatExponent == arg1type.FloatExponent && arg0type.FloatSignificand == arg1type.FloatSignificand) { + //So we never call arg0type.Unify, but it should be ok + return Type.GetFloatType(arg0type.FloatSignificand, arg0type.FloatExponent); + } } goto BAD_TYPE; case Opcode.Div: @@ -1741,11 +1799,11 @@ (arg1type.Unify(Type.Int) || arg1type.Unify(Type.Real))) { return Type.Real; } - if (arg0type.IsFloat && arg0type.Unify(arg1type)) { - return Type.GetFloatType(arg0.Type.FloatSignificand, arg0.Type.FloatExponent); - } - if (arg1type.IsFloat && arg1type.Unify(arg0type)) { - return Type.GetFloatType(arg1.Type.FloatSignificand, arg1.Type.FloatExponent); + if (arg0type.IsFloat && arg1type.IsFloat) { + if (arg0type.FloatExponent == arg1type.FloatExponent && arg0type.FloatSignificand == arg1type.FloatSignificand) { + //So we never call arg0type.Unify, but it should be ok + return Type.GetFloatType(arg0type.FloatSignificand, arg0type.FloatExponent); + } } goto BAD_TYPE; case Opcode.Pow: @@ -1779,8 +1837,10 @@ if (arg0type.Unify(Type.Real) && arg1type.Unify(Type.Real)) { return Type.Bool; } - if ((arg0type.IsFloat && arg0type.Unify(arg1type)) || (arg1type.IsFloat && arg1type.Unify(arg0type))) { - return Type.Bool; + if (arg0type.IsFloat && arg1type.IsFloat) { + if (arg0type.FloatExponent == arg1type.FloatExponent && arg0type.FloatSignificand == arg1type.FloatSignificand) { + return Type.Bool; + } } goto BAD_TYPE; case Opcode.And: @@ -2268,8 +2328,7 @@ public class ArithmeticCoercion : IAppliable { public enum CoercionType { ToInt, - ToReal, - ToFloat + ToReal } private IToken/*!*/ tok; @@ -3048,6 +3107,10 @@ v.Register(rc); v.Resolve(rc); } + foreach (Variable/*!*/ v in LocVars) { + Contract.Assert(v != null); + v.ResolveWhere(rc); + } rc.PushProcedureContext(); foreach (Block/*!*/ b in Blocks) { diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/AbsyQuant.cs boogie-2.4.1+dfsg/Source/Core/AbsyQuant.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/AbsyQuant.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/AbsyQuant.cs 2019-10-25 18:17:05.000000000 +0000 @@ -45,10 +45,10 @@ } } [ContractClass(typeof(BinderExprContracts))] - public abstract class BinderExpr : Expr { + public abstract class BinderExpr : Expr, ICarriesAttributes { public List/*!*/ TypeParameters; public List/*!*/ Dummies; - public QKeyValue Attributes; + public QKeyValue Attributes { get; set; } // FIXME: Protect the above Fields public Expr _Body; public Expr/*!*/ Body { @@ -228,10 +228,10 @@ public override void ComputeFreeVariables(Set freeVars) { //Contract.Requires(freeVars != null); - ComputeBinderFreeVariables(TypeParameters, Dummies, Body, Attributes, freeVars); + ComputeBinderFreeVariables(TypeParameters, Dummies, Body, null, Attributes, freeVars); } - public static void ComputeBinderFreeVariables(List typeParameters, List dummies, Expr body, QKeyValue attributes, Set freeVars) { + public static void ComputeBinderFreeVariables(List typeParameters, List dummies, Expr body, Trigger triggers, QKeyValue attributes, Set freeVars) { Contract.Requires(dummies != null); Contract.Requires(body != null); @@ -240,6 +240,11 @@ Contract.Assert(!freeVars[v]); } body.ComputeFreeVariables(freeVars); + for (var trig = triggers; trig != null; trig = trig.Next) { + foreach (var e in trig.Tr) { + e.ComputeFreeVariables(freeVars); + } + } for (var a = attributes; a != null; a = a.Next) { foreach (var o in a.Params) { var e = o as Expr; @@ -576,7 +581,13 @@ } public override int GetHashCode() { - throw new NotImplementedException(); + int hash = 17; + hash = hash * 23 + tr.GetHashCode(); + hash = hash * 23 + Pos.GetHashCode(); + if (Next != null) { + hash = hash * 23 + Next.GetHashCode(); + } + return hash; } } @@ -815,6 +826,10 @@ } } + public override void ComputeFreeVariables(Set freeVars) { + //Contract.Requires(freeVars != null); + ComputeBinderFreeVariables(TypeParameters, Dummies, Body, Triggers, Attributes, freeVars); + } public override void Typecheck(TypecheckingContext tc) { //Contract.Requires(tc != null); @@ -950,4 +965,176 @@ } } + + public class LetExpr : Expr, ICarriesAttributes { + public LetExpr(IToken/*!*/ tok, List/*!*/ dummies, List rhss, QKeyValue kv, Expr/*!*/ body) + : base(tok, false) { + Contract.Requires(tok != null); + Contract.Requires(dummies != null); + Contract.Requires(rhss != null); + Contract.Requires(body != null); + Contract.Requires(dummies.Count > 0); + Contract.Requires(rhss.Count > 0); + Dummies = dummies; + Rhss = rhss; + Attributes = kv; + Body = body; + } + + public override void Resolve(ResolutionContext rc) { + //Contract.Requires(rc != null); + + if (Dummies.Count != Rhss.Count) { + rc.Error(this.tok, "number of left-hand sides does not match number of right-hand sides"); + } + + foreach (var e in Rhss) { + e.Resolve(rc); + } + + rc.PushVarContext(); + foreach (var v in Dummies) { + Contract.Assert(v != null); + v.Register(rc); + v.Resolve(rc); + } + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Resolve(rc); + } + Body.Resolve(rc); + rc.PopVarContext(); + } + + public override void Typecheck(TypecheckingContext tc) { + //Contract.Requires(tc != null); + foreach (var e in Rhss) { + e.Typecheck(tc); + } + Contract.Assert(Dummies.Count == Rhss.Count); // enforced by resolution + for (var i = 0; i < Dummies.Count; i++) { + var lhs = Dummies[i]; + var rhs = Rhss[i]; + lhs.TypedIdent.Type = rhs.Type; + } + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Typecheck(tc); + } + Body.Typecheck(tc); + Contract.Assert(Body.Type != null); // follows from postcondition of Expr.Typecheck + this.Type = Body.Type; + } + + public override Type/*!*/ ShallowType { + get { + Contract.Ensures(Contract.Result() != null); + return Body.ShallowType; + } + } + + public override Absy StdDispatch(StandardVisitor visitor) { + return visitor.VisitLetExpr(this); + } + public List/*!*/ Dummies; + public IList/*!*/ Rhss; + public QKeyValue Attributes { get; set; } + public Expr Body; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(Dummies != null); + Contract.Invariant(Rhss != null); + Contract.Invariant(Body != null); + } + + [Pure] + [Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object obj) { + if (!(obj is LetExpr)) { + return false; + } + + var other = (LetExpr) obj; + + return this.Dummies.SequenceEqual(other.Dummies) + && this.Rhss.SequenceEqual(other.Rhss) + && object.Equals(this.Attributes, other.Attributes) + && object.Equals(this.Body, other.Body); + } + + [Pure] + public override int GetHashCode() { + return ComputeHashCode(); + } + + [Pure] + public override int ComputeHashCode() { + // Note, we don't hash triggers and attributes + + // DO NOT USE Dummies.GetHashCode() because we want structurally + // identical Expr to have the same hash code **not** identical references + // to have the same hash code. + int h = 0; + foreach (var dummyVar in this.Dummies) { + h = ( 53 * h ) + dummyVar.GetHashCode(); + } + + h ^= this.Body.GetHashCode(); + + return h; + } + + public override void Emit(TokenTextWriter stream, int contextBindingStrength, bool fragileContext) { + //Contract.Requires(stream != null); + stream.push(); + stream.Write(this, "(var "); + + string sep = ""; + stream.push(); + foreach (var v in Dummies) { + stream.Write("{0}", sep); + v.EmitVitals(stream, 0, true, false); + sep = ", "; + stream.sep(); + } + stream.pop(); + + stream.Write(" := "); + this.Rhss.Emit(stream); + stream.Write("; "); + stream.sep(); + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) { + kv.Emit(stream); + stream.Write(" "); + } + stream.sep(); + + this.Body.Emit(stream); + stream.Write(")"); + stream.pop(); + } + + public override void ComputeFreeVariables(Set freeVars) { + //Contract.Requires(freeVars != null); + + foreach (var v in Dummies) { + Contract.Assert(v != null); + Contract.Assert(!freeVars[v]); + } + Body.ComputeFreeVariables(freeVars); + for (var a = Attributes; a != null; a = a.Next) { + foreach (var o in a.Params) { + var e = o as Expr; + if (e != null) { + e.ComputeFreeVariables(freeVars); + } + } + } + foreach (var v in Dummies) { + freeVars.AddRange(v.TypedIdent.Type.FreeVariables); + } + freeVars.RemoveRange(Dummies); + foreach (var e in Rhss) { + e.ComputeFreeVariables(freeVars); + } + } + } } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/AbsyType.cs boogie-2.4.1+dfsg/Source/Core/AbsyType.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/AbsyType.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/AbsyType.cs 2019-10-25 18:17:05.000000000 +0000 @@ -254,6 +254,11 @@ return false; } } + public virtual bool IsRMode { + get { + return false; + } + } public virtual bool IsVariable { get { @@ -365,6 +370,7 @@ public static readonly Type/*!*/ Int = new BasicType(SimpleType.Int); public static readonly Type/*!*/ Real = new BasicType(SimpleType.Real); public static readonly Type/*!*/ Bool = new BasicType(SimpleType.Bool); + public static readonly Type/*!*/ RMode = new BasicType(SimpleType.RMode); private static BvType[] bvtypeCache; static public BvType GetBvType(int sz) { @@ -909,6 +915,8 @@ return "real"; case SimpleType.Bool: return "bool"; + case SimpleType.RMode: + return "rmode"; } Debug.Assert(false, "bad type " + T); { @@ -1034,6 +1042,13 @@ return this.T == SimpleType.Bool; } } + public override bool IsRMode + { + get + { + return this.T == SimpleType.RMode; + } + } public override Absy StdDispatch(StandardVisitor visitor) { //Contract.Requires(visitor != null); @@ -1481,7 +1496,8 @@ public override Type ResolveType(ResolutionContext rc) { //Contract.Requires(rc != null); Contract.Ensures(Contract.Result() != null); - // first case: the type name denotes a bitvector-type or float-type + // first case: the type name denotes a bitvector-type, float-type, or rmode-type + if (Name.StartsWith("bv") && Name.Length > 2) { bool is_bv = true; for (int i = 2; i < Name.Length; ++i) { @@ -1522,6 +1538,17 @@ } } + if (Name.Equals("rmode")) + { + if (Arguments.Count > 0) + { + rc.Error(this, + "rounding mode type must not be applied to arguments: {0}", + Name); + } + return Type.RMode; + } + // second case: the identifier is resolved to a type variable TypeVariable var = rc.LookUpTypeBinder(Name); if (var != null) { @@ -2122,6 +2149,23 @@ return p != null && p.IsFloat; } } + public override int FloatExponent { + get { + Type p = ProxyFor; + if (p == null || !p.IsFloat) + return base.FloatExponent; //Shouldn't happen, so get an unreachable exception + return p.FloatExponent; + } + } + public override int FloatSignificand { + get { + Type p = ProxyFor; + if (p == null || !p.IsFloat) + return base.FloatSignificand; //Shouldn't happen, so get an unreachable exception + return p.FloatSignificand; + } + } + public override bool IsBool { get { Type p = ProxyFor; @@ -2129,6 +2173,15 @@ } } + public override bool IsRMode + { + get + { + Type p = ProxyFor; + return p != null && p.IsRMode; + } + } + public override bool IsVariable { get { Type p = ProxyFor; @@ -2984,11 +3037,32 @@ return ExpandedType.IsFloat; } } + public override int FloatExponent + { + get + { + return ExpandedType.FloatExponent; + } + } + + public override int FloatSignificand + { + get + { + return ExpandedType.FloatSignificand; + } + } + public override bool IsBool { get { return ExpandedType.IsBool; } } + public override bool IsRMode { + get { + return ExpandedType.IsRMode; + } + } public override bool IsVariable { get { @@ -3758,7 +3832,8 @@ public enum SimpleType { Int, Real, - Bool + Bool, + RMode }; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/BoogiePL.atg boogie-2.4.1+dfsg/Source/Core/BoogiePL.atg --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/BoogiePL.atg 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/BoogiePL.atg 2019-10-25 18:17:05.000000000 +0000 @@ -63,17 +63,18 @@ Parser parser = new Parser(scanner, errors, false); parser.Parse(); - if (parser.errors.count == 0) + if (errors.count == 0) { - program = parser.Pgm; - program.ProcessDatatypeConstructors(); - return 0; + parser.Pgm.ProcessDatatypeConstructors(errors); } - else + if (errors.count == 0) + { + program = parser.Pgm; + } else { program = null; - return parser.errors.count; } + return errors.count; } public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors, bool disambiguation) @@ -97,7 +98,7 @@ public BigNum Lower; public BigNum Upper; public BvBounds(IToken/*!*/ tok, BigNum lower, BigNum upper) - : base(tok) { + : base(tok, /*immutable=*/ false) { Contract.Requires(tok != null); this.Lower = lower; this.Upper = upper; @@ -112,12 +113,17 @@ Contract.Assert(false);throw new cce.UnreachableException(); } public override void ComputeFreeVariables(GSet/*!*/ freeVars) { Contract.Assert(false);throw new cce.UnreachableException(); } + public override int ComputeHashCode() + { + return base.GetHashCode(); + } } /*--------------------------------------------------------------------------*/ CHARACTERS letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". digit = "0123456789". + hexdigit = "0123456789ABCDEFabcdef". special = "'~#$^_.?`". glyph = "`~!@#$%^&*()-_=+[{]}|;:',<.>/?\\". @@ -145,7 +151,7 @@ decimal = digit {digit} 'e' [ '-' ] digit {digit} . dec_float = digit {digit} '.' digit {digit} [ 'e' [ '-' ] digit {digit} ] . - float = [ '-' ] digit {digit} 'e' [ '-' ] digit {digit} 'f' digit {digit} 'e' digit {digit} + float = [ '-' ] '0' 'x' hexdigit {hexdigit} '.' hexdigit {hexdigit} 'e' [ '-' ] digit {digit} 'f' digit {digit} 'e' digit {digit} | '0' 'N' 'a' 'N' digit {digit} 'e' digit {digit} | '0' 'n' 'a' 'n' digit {digit} 'e' digit {digit} | '0' '+' 'o' 'o' digit {digit} 'e' digit {digit} @@ -237,9 +243,8 @@ ")" . -BoundVars<.IToken/*!*/ x, out List/*!*/ ds.> +BoundVars<.out List/*!*/ ds.> = (. - Contract.Requires(x != null); Contract.Ensures(Contract.ValueAtReturn(out ds) != null); List/*!*/ tyds = new List(); ds = new List(); @@ -885,6 +890,7 @@ List/*!*/ lhss; List/*!*/ rhss; List/*!*/ indexes; + QKeyValue kv = null; .) Ident (. x = t; .) ( ":" (. c = null; label = x; .) @@ -903,12 +909,13 @@ } ":=" (. x = t; /* use location of := */ .) + { Attribute } Expression (. rhss = new List (); rhss.Add(e0); .) { "," Expression (. rhss.Add(e0); .) } - ";" (. c = new AssignCmd(x, lhss, rhss); .) + ";" (. c = new AssignCmd(x, lhss, rhss, kv); .) ) . @@ -1268,6 +1275,11 @@ .) ( "false" (. e = new LiteralExpr(t, false); .) | "true" (. e = new LiteralExpr(t, true); .) + | ("roundNearestTiesToEven" | "RNE") (. e = new LiteralExpr(t, RoundingMode.RNE); .) + | ("roundNearestTiesToAway" | "RNA") (. e = new LiteralExpr(t, RoundingMode.RNA); .) + | ("roundTowardPositive" | "RTP") (. e = new LiteralExpr(t, RoundingMode.RTP); .) + | ("roundTowardNegative" | "RTN") (. e = new LiteralExpr(t, RoundingMode.RTN); .) + | ("roundTowardZero" | "RTZ") (. e = new LiteralExpr(t, RoundingMode.RTZ); .) | Nat (. e = new LiteralExpr(t, bn); .) | Dec (. e = new LiteralExpr(t, bd); .) | Float (. e = new LiteralExpr(t, bf); .) @@ -1313,6 +1325,7 @@ SemErr("triggers not allowed in lambda expressions"); if (typeParams.Count + ds.Count > 0) e = new LambdaExpr(x, typeParams, ds, kv, e); .) + | LetExpr ) ")" | IfThenElseExpression @@ -1443,9 +1456,9 @@ .) ( TypeParams - [ BoundVars ] + [ BoundVars ] | - BoundVars + BoundVars ) QSep { AttributeOrTrigger } @@ -1457,6 +1470,42 @@ Lambda = "lambda" | '\u03bb'. QSep = "::" | '\u2022'. +LetExpr<.out Expr/*!*/ letexpr.> += (. IToken tok; + Variable v; + var ds = new List(); + Expr e0; + var rhss = new List(); + QKeyValue kv = null; + Expr body; + .) + "var" (. tok = t; .) + LetVar (. ds.Add(v); .) + { "," + LetVar (. ds.Add(v); .) + } + ":=" + Expression (. rhss.Add(e0); .) + { "," + Expression (. rhss.Add(e0); .) + } + ";" + { Attribute } + Expression (. letexpr = new LetExpr(tok, ds, rhss, kv, body); .) + . + +LetVar<.out Variable/*!*/ v.> += (. QKeyValue kv = null; + IToken id; + .) + { Attribute } + Ident + (. + var tyd = new TypedIdent(id, id.val, dummyType/*will be replaced during type checking*/, null); + v = new BoundVariable(tyd.tok, tyd, kv); + .) + . + /*------------------------------------------------------------------------*/ Ident =(.Contract.Ensures(Contract.ValueAtReturn(out x) != null);.) diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/CivlAttributes.cs boogie-2.4.1+dfsg/Source/Core/CivlAttributes.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/CivlAttributes.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/CivlAttributes.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Boogie +{ + public static class CivlAttributes + { + public const string LAYER = "layer"; + + public const string YIELDS = "yields"; + public const string YIELD_ASSERT = "yield_assert"; + + public const string ATOMIC = "atomic"; + public const string LEFT = "left"; + public const string RIGHT = "right"; + public const string BOTH = "both"; + + public static string REFINES = "refines"; + + public const string TERMINATES = "terminates"; + + public const string LINEAR = "linear"; + public const string LINEAR_IN = "linear_in"; + public const string LINEAR_OUT = "linear_out"; + + public const string PURE = "pure"; + + public const string BACKWARD = "backward"; + public const string WITNESS = "witness"; + + public static bool RemoveAttribute(ICarriesAttributes obj, Func cond) + { + QKeyValue curr = obj.Attributes; + bool removed = false; + + while (curr != null && cond(curr)) + { + curr = curr.Next; + removed = true; + } + obj.Attributes = curr; + while (curr != null) + { + QKeyValue next = curr.Next; + while (next != null && cond(next)) + { + next = next.Next; + removed = true; + } + curr.Next = next; + curr = next; + } + + return removed; + } + + public static void RemoveYieldsAttribute(ICarriesAttributes obj) + { + RemoveAttribute(obj, kv => kv.Key == YIELDS); + } + + public static void RemoveMoverAttribute(ICarriesAttributes obj) + { + RemoveAttribute(obj, + kv => kv.Key == ATOMIC || kv.Key == LEFT || kv.Key == RIGHT || kv.Key == BOTH); + } + + public static void RemoveLayerAttribute(ICarriesAttributes obj) + { + RemoveAttribute(obj, kv => kv.Key == LAYER); + } + + public static void RemoveLinearAttribute(ICarriesAttributes obj) + { + RemoveAttribute(obj, + kv => kv.Key == LINEAR || kv.Key == LINEAR_IN || kv.Key == LINEAR_OUT); + } + + public static void RemoveRefinesAttribute(ICarriesAttributes obj) + { + RemoveAttribute(obj, kv => kv.Key == REFINES); + } + + public static void RemoveWitnessAttribute(ICarriesAttributes obj) + { + RemoveAttribute(obj, kv => kv.Key == WITNESS); + } + + public static void DesugarYieldAssert(Program program) + { + foreach (var proc in program.Procedures) + { + if (RemoveAttribute(proc, kv => kv.Key == YIELD_ASSERT)) + { + proc.AddAttribute(YIELDS); + foreach (var requires in proc.Requires) + { + var ensures = new Ensures(false, requires.Condition); + ensures.Attributes = requires.Attributes; + proc.Ensures.Add(ensures); + } + } + } + + foreach (var impl in program.Implementations) + { + RemoveAttribute(impl, kv => kv.Key == YIELD_ASSERT); + } + } + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/CommandLineOptions.cs boogie-2.4.1+dfsg/Source/Core/CommandLineOptions.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/CommandLineOptions.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/CommandLineOptions.cs 2019-10-25 18:17:05.000000000 +0000 @@ -97,7 +97,7 @@ if (pattern != null) { pattern = pattern.Replace("@PREFIX@", logPrefix).Replace("@TIME@", fileTimestamp); string fn = Files.Count == 0 ? "" : Files[Files.Count - 1]; - fn = fn.Replace('/', '-').Replace('\\', '-'); + fn = fn.Replace(':', '-').Replace('/', '-').Replace('\\', '-'); pattern = pattern.Replace("@FILE@", fn); } } @@ -416,10 +416,13 @@ public int DoomStrategy = -1; public bool DoomRestartTP = false; public bool PrintDesugarings = false; + public bool PrintLambdaLifting = false; + public bool FreeVarLambdaLifting = false; public string SimplifyLogFilePath = null; public bool PrintInstrumented = false; public bool InstrumentWithAsserts = false; public string ProverPreamble = null; + public bool WarnNotEliminatedVars = false; public enum InstrumentationPlaces { LoopHeaders, @@ -490,6 +493,7 @@ public string Z3ExecutableName = null; public string CVC4ExecutablePath = null; public int KInductionDepth = -1; + public int EnableUnSatCoreExtract = 0; private string/*!*/ _logPrefix = ""; @@ -530,6 +534,7 @@ } public ShowEnvironment ShowEnv = ShowEnvironment.DuringPrint; public bool DontShowLogo = false; + public bool ShowVerifiedProcedureCount = true; [ContractInvariantMethod] void ObjectInvariant3() { Contract.Invariant(-1 <= LoopFrameConditions && LoopFrameConditions < 3); @@ -669,6 +674,7 @@ public int SimplifyProverMatchDepth = -1; // -1 means not specified public int ProverKillTime = -1; // -1 means not specified + public int Resourcelimit = 0; // default to 0 public int SmokeTimeout = 10; // default to 10s public int ProverCCLimit = 5; public bool z3AtFlag = true; @@ -799,10 +805,12 @@ // Static constructor static CommandLineOptions() { if (System.Type.GetType("Mono.Runtime") == null) { // MONO +#if !COREFX_SUBSET TraceListenerCollection/*!*/ dbl = Debug.Listeners; Contract.Assert(dbl != null); Contract.Assume(cce.IsPeerConsistent(dbl)); // hangs off static field dbl.Add(new DefaultTraceListener()); +#endif } } @@ -1010,6 +1018,14 @@ return true; } + case "printVerifiedProceduresCount": { + int n = 0; + if (ps.GetNumericArgument(ref n, 2)) { + ShowVerifiedProcedureCount = n != 0; + } + return true; + } + case "loopUnroll": ps.GetNumericArgument(ref LoopUnrollCount); return true; @@ -1342,6 +1358,12 @@ RecursionBound = Int32.Parse(cce.NonNull(args[ps.i])); } return true; + case "enableUnSatCoreExtraction": + if (ps.ConfirmArgumentCount(1)) + { + EnableUnSatCoreExtract = Int32.Parse(cce.NonNull(args[ps.i])); + } + return true; case "stackDepthBound": if (ps.ConfirmArgumentCount(1)) { @@ -1492,6 +1514,10 @@ ps.GetNumericArgument(ref ProverKillTime); return true; + case "rlimit": + ps.GetNumericArgument(ref Resourcelimit); + return true; + case "timeLimitPerAssertionInPercent": ps.GetNumericArgument(ref TimeLimitPerAssertionInPercent, a => 0 < a); return true; @@ -1517,8 +1543,7 @@ UseSmtOutputFormat = true; } return true; - } - + } case "z3opt": if (ps.ConfirmArgumentCount(1)) { AddZ3Option(cce.NonNull(args[ps.i])); @@ -1581,6 +1606,7 @@ } if (ps.CheckBooleanFlag("printDesugared", ref PrintDesugarings) || + ps.CheckBooleanFlag("printLambdaLifting", ref PrintLambdaLifting) || ps.CheckBooleanFlag("printInstrumented", ref PrintInstrumented) || ps.CheckBooleanFlag("printWithUniqueIds", ref PrintWithUniqueASTIds) || ps.CheckBooleanFlag("wait", ref Wait) || @@ -1628,7 +1654,9 @@ ps.CheckBooleanFlag("verifySeparately", ref VerifySeparately) || ps.CheckBooleanFlag("trustAtomicityTypes", ref TrustAtomicityTypes) || ps.CheckBooleanFlag("trustNonInterference", ref TrustNonInterference) || - ps.CheckBooleanFlag("useBaseNameForFileName", ref UseBaseNameForFileName) + ps.CheckBooleanFlag("useBaseNameForFileName", ref UseBaseNameForFileName) || + ps.CheckBooleanFlag("freeVarLambdaLifting", ref FreeVarLambdaLifting) || + ps.CheckBooleanFlag("warnNotEliminatedVars", ref WarnNotEliminatedVars) ) { // one of the boolean flags matched return true; @@ -1769,8 +1797,8 @@ With /inline:assume call is replaced with ""assume false"" once inlining depth is reached. With /inline:assert call is replaced with ""assert false"" once inlining depth is reached. With /inline:spec call is left as is once inlining depth is reached. - With the above three options, methods with the attribute {:inline N} are not verified. With /inline:none the entire attribute is ignored. + With /inline:assume and /inline:assert options, methods with the attribute {:inline N} are not verified. {:verify false} Skip verification of an implementation. @@ -1799,6 +1827,9 @@ {:timeLimit N} Set the time limit for a given implementation. + {:rlimit N} + Set the Z3 resource limit for a given implementation. + ---- On functions ---------------------------------------------------------- {:builtin ""spec""} @@ -1853,6 +1884,9 @@ /env: print command line arguments 0 - never, 1 (default) - during BPL print and prover log, 2 - like 1 and also to standard output + /printVerifiedProceduresCount: + 0 - no + 1 (default) - yes /wait await Enter from keyboard before terminating program /xml: also produce output in XML format to @@ -1882,6 +1916,13 @@ identifies variables /printUnstructured : with /print option, desugars all structured statements /printDesugared : with /print option, desugars calls + /printLambdaLifting : with /print option, desugars lambda lifting + + /freeVarLambdaLifting : Boogie's lambda lifting transforms the bodies of lambda + expressions into templates with holes. By default, holes + are maximally large subexpressions that do not contain + bound variables. This option performs a form of lambda + lifting in which holes are the lambda's free variables. /overlookTypeErrors : skip any implementation with resolution or type checking errors @@ -1960,6 +2001,19 @@ /break launch and break into debugger + ---- CIVL options ---------------------------------------------------------- + + /trustAtomicityTypes + do not verify atomic action declarations + /trustNonInterference + do not perform noninterference checks + /trustLayersUpto: + do not verify layers and below + /trustLayersDownto: + do not verify layers and above + /CivlDesugaredFile: + print plain Boogie program to + ---- Verification-condition generation options ----------------------------- /liveVariableAnalysis: @@ -1977,6 +2031,11 @@ to the new source locations for errors and their related locations (but not /errorTrace and CaptureState locations) + /traceCaching: + 0 (default) - none + 1 - for testing + 2 - for benchmarking + 3 - for testing, benchmarking, and debugging /verifySeparately verify each input program separately /removeEmptyBlocks: @@ -2105,6 +2164,8 @@ /timeLimit: Limit the number of seconds spent trying to verify each procedure + /rlimit: + Limit the Z3 resource spent trying to verify each procedure /errorTrace: 0 - no Trace labels in the error output, 1 (default) - include useful Trace labels in error output, diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Core.csproj boogie-2.4.1+dfsg/Source/Core/Core.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Core.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/Core.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,7 +10,7 @@ Properties Core BoogieCore - v4.0 + v4.5 512 1 true @@ -34,7 +34,7 @@ false false true - Client + true @@ -74,6 +74,7 @@ Full %28none%29 AllRules.ruleset + false pdbonly @@ -83,6 +84,7 @@ prompt 4 AllRules.ruleset + false true @@ -99,6 +101,7 @@ true 4 false + false true @@ -143,6 +146,7 @@ 0 4 false + false true @@ -152,6 +156,7 @@ AnyCPU prompt AllRules.ruleset + false @@ -165,12 +170,16 @@ + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Core-NetCore.csproj boogie-2.4.1+dfsg/Source/Core/Core-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Core-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/Core-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,30 @@ + + + + Library + BoogieCore + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + + + + false + + + + + version.cs + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/DeadVarElim.cs boogie-2.4.1+dfsg/Source/Core/DeadVarElim.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/DeadVarElim.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/DeadVarElim.cs 2019-10-25 18:17:05.000000000 +0000 @@ -111,9 +111,9 @@ } foreach (Procedure x in yieldingProcs) { - if (!QKeyValue.FindBoolAttribute(x.Attributes, "yields")) + if (!QKeyValue.FindBoolAttribute(x.Attributes, CivlAttributes.YIELDS)) { - x.AddAttribute("yields"); + x.AddAttribute(CivlAttributes.YIELDS); } } @@ -326,6 +326,21 @@ } return node; } + + public static IEnumerable Collect(Absy node) + { + var collector = new VariableCollector(); + collector.Visit(node); + return collector.usedVars; + } + + public static IEnumerable Collect(IEnumerable nodes) + { + var collector = new VariableCollector(); + foreach (var node in nodes) + collector.Visit(node); + return collector.usedVars; + } } public class BlockCoalescer : ReadOnlyVisitor { diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Duplicator.cs boogie-2.4.1+dfsg/Source/Core/Duplicator.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Duplicator.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/Duplicator.cs 2019-10-25 18:17:05.000000000 +0000 @@ -290,6 +290,10 @@ return impl; } + public override Expr VisitLetExpr(LetExpr node) { + Contract.Ensures(Contract.Result() != null); + return base.VisitLetExpr((LetExpr)node.Clone()); + } public override Expr VisitLiteralExpr(LiteralExpr node) { //Contract.Requires(node != null); Contract.Ensures(Contract.Result() != null); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Inline.cs boogie-2.4.1+dfsg/Source/Core/Inline.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Inline.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/Inline.cs 2019-10-25 18:17:05.000000000 +0000 @@ -479,7 +479,10 @@ LocalVariable localVar = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, GetProcVarName(proc.Name, mVar.Name), mVar.TypedIdent.Type)); newLocalVars.Add(localVar); IdentifierExpr ie = new IdentifierExpr(Token.NoToken, localVar); - substMapOld.Add(mVar, ie); + if (!substMapOld.ContainsKey(mVar)) { + substMapOld.Add(mVar, ie); + } + // FIXME why are we doing this? the modifies list should already include them. // add the modified variable to the modifies list of the procedure if (!newModifies.Contains(mie)) { @@ -767,4 +770,4 @@ } } } // end class CodeCopier -} // end namespace \ No newline at end of file +} // end namespace diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/LambdaHelper.cs boogie-2.4.1+dfsg/Source/Core/LambdaHelper.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/LambdaHelper.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/LambdaHelper.cs 2019-10-25 18:17:05.000000000 +0000 @@ -3,13 +3,13 @@ // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- + +using Core; + namespace Microsoft.Boogie { using System; - using System.IO; - using System.Collections; using System.Collections.Generic; - using System.Diagnostics; using System.Diagnostics.Contracts; using Set = GSet; // for the purposes here, "object" really means "either Variable or TypeVariable" @@ -37,6 +37,38 @@ return program; } + /// + /// Performs lambda lifting on all anonymous functions in a program. + /// Lambda lifting transforms a lambda abstraction (lambda x : T :: t) of type T -> U + /// into a Boogie map of type [T] U as follows: + /// + /// + /// Certain subexpressions e1, ..., e_n (which have types T1, ..., T_n) + /// of the lambda body [t] are replaced with bound variables b1, ..., b_n of the same types, + /// yielding a new lambda body [u], which represents a lifted lambda (lambda x : T :: u). + /// + /// + /// The original lambda is replaced with a function call f(e1, ..., en) where f is a function + /// with n parameters and return type [T] U (i.e. the function returns a map). + /// + /// + /// The implementation of the lambda is defined through an axiom that states that + /// f(b1, ..., b_n)[x] == u. + /// + /// + /// Lambda lifting algorithms differ based on what kind of subexpressions E_i are "lifted" out + /// of the lambda: + /// + /// + /// lifts all free variables of a lambda; + /// + /// + /// LambdaVisitor.LambdaLifterMaxHoles() lifts a lambda's maximally large subexpressions that are + /// free of bound variables. + /// + /// is used by default whereas LambdaLiftingFreeVars + /// is used with the command-line option /freeVarLambdaLifting. + /// public static void ExpandLambdas(Program prog) { Contract.Requires(prog != null); List/*!*/ axioms; @@ -77,6 +109,32 @@ return baseResult; // apparently, the base visitor already turned the lambda into something else } + return CommandLineOptions.Clo.FreeVarLambdaLifting ? LiftLambdaFreeVars(lambda) : LiftLambdaMaxHoles(lambda); + } + + /// + /// Performs lambda lifting (see ) by replacing the lambda's + /// free variables with bound ones. + /// + /// A lambda expression + /// (lambda x1: T1 ... x_n: T_n :: t) + /// where t contains the free variables y1, ..., y_m. + /// + /// + /// + /// + /// A function application f(y1, ..., y_m) where f's body is defined to be the result of + /// replacing the free variables y1, ..., y_m in t with bound variables + /// b1, ..., b_m. + /// + /// + /// Adds a definition and axiom for f to and . + /// Memoizes f as the lifted lambda for lambda. + /// + /// + /// + private Expr LiftLambdaFreeVars(LambdaExpr lambda) { + // We start by getting rid of any use of "old" inside the lambda. This is done as follows. // For each variable "g" occurring inside lambda as "old(... g ...)", create a new name "og". // Replace each old occurrence of "g" with "og", removing the enclosing "old" wrappers. @@ -128,7 +186,7 @@ // compute the free variables of the lambda expression, but with lambdaBody instead of lambda.Body Set freeVars = new Set(); - BinderExpr.ComputeBinderFreeVariables(lambda.TypeParameters, lambda.Dummies, lambdaBody, lambdaAttrs, freeVars); + BinderExpr.ComputeBinderFreeVariables(lambda.TypeParameters, lambda.Dummies, lambdaBody, null, lambdaAttrs, freeVars); foreach (object o in freeVars) { // 'o' is either a Variable or a TypeVariable. @@ -219,6 +277,58 @@ return call; } + + /// + /// Performs lambda lifting (see ) by replacing with bound variables + /// maximally large subexpressions of a lambda that do not contain any of the lambda's bound variables. + /// + /// A lambda expression + /// (lambda x1: T1 ... x_n: T_n :: t) + /// where t contains the subexpressions e1, ..., e_m. These are maximally large + /// subexpressions that do not contain the lambda's bound variables. + /// + /// + /// + /// + /// A function application f(y1, ..., y_m) where f's body is defined to be the result of + /// replacing the expressions e1, ..., e_m in t with bound variables + /// b1, ..., b_m. + /// + /// + /// Adds a definition and axiom for f to and . + /// Memoizes f as the lifted lambda for lambda. + /// + /// + /// + private Expr LiftLambdaMaxHoles(LambdaExpr lambda) { + + // We start by getting rid of `old` expressions. Instead, we replace the free variables `x_i` that are + // nested inside of `old` expressions with `old(x_i)` expressions. + var oldFinder = new OldFinder(); + oldFinder.Visit(lambda); + var oldSubst = new Dictionary(); + foreach (var v in oldFinder.FreeOldVars) if (v is GlobalVariable g) { + oldSubst.Add(g, new OldExpr(g.tok, new IdentifierExpr(g.tok, g)) {Type = g.TypedIdent.Type}); + } + var lambdaBody = Substituter.ApplyReplacingOldExprs( + Substituter.SubstitutionFromHashtable(new Dictionary()), + Substituter.SubstitutionFromHashtable(oldSubst), + lambda.Body); + var lambdaAttrs = Substituter.ApplyReplacingOldExprs( + Substituter.SubstitutionFromHashtable(new Dictionary()), + Substituter.SubstitutionFromHashtable(oldSubst), + lambda.Attributes); + var newLambda = + new LambdaExpr(lambda.tok, lambda.TypeParameters, lambda.Dummies, lambdaAttrs, lambdaBody) { + Type = lambda.Type + }; + + // We perform lambda lifting on the resulting lambda which now contains only `old` expressions of the form + // `old(x)` where `x` is a variable that is free in the lambda. + return new MaxHolesLambdaLifter( + newLambda, liftedLambdas, FreshLambdaFunctionName(), lambdaFunctions, lambdaAxioms).VisitLambdaExpr(newLambda); + } + public override Cmd VisitCallCmd(CallCmd node) { var baseResult = base.VisitCallCmd(node); node = baseResult as CallCmd; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/LambdaLiftingMaxHolesFiller.cs boogie-2.4.1+dfsg/Source/Core/LambdaLiftingMaxHolesFiller.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/LambdaLiftingMaxHolesFiller.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/LambdaLiftingMaxHolesFiller.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using Microsoft.Boogie; + +namespace Core { + +/// +/// Traverses an expression, replacing all holes with bound variables taken from the +/// queue. +/// +class LambdaLiftingMaxHolesFiller : StandardVisitor { + private readonly List _holes; + private readonly QKeyValue _lambdaAttrs; + private readonly Queue _replDummies; + + private LambdaLiftingMaxHolesFiller(List holes, IEnumerable replDummies, QKeyValue lambdaAttrs) { + _holes = holes; + _lambdaAttrs = lambdaAttrs; + _replDummies = new Queue(replDummies); + } + + public static Expr Fill(List holes, + List replDummies, + Expr expr, QKeyValue lambdaAttrs) { + return new LambdaLiftingMaxHolesFiller(holes, replDummies, lambdaAttrs).VisitExpr(expr); + } + + private bool ShouldBeReplaced(Absy node) { + return _holes.Contains(node); + } + + private bool GetReplacementVariable(Absy node, out Variable variable) { + if (_replDummies.Count > 0 && ShouldBeReplaced(node)) { + variable = _replDummies.Dequeue(); + return true; + } + variable = null; + return false; + } + + public override Expr VisitLambdaExpr(LambdaExpr node) { + var attributes =_lambdaAttrs == null ? null : VisitQKeyValue(_lambdaAttrs); + var body = VisitExpr(node.Body); + return new LambdaExpr(node.tok, node.TypeParameters, node.Dummies, attributes, body); + } + + public override Variable VisitVariable(Variable node) { + if (GetReplacementVariable(new IdentifierExpr(node.tok, node), out var variable)) return variable; + return base.VisitVariable(node); + } + + public override Expr VisitExistsExpr(ExistsExpr node) { + if (GetReplacementVariable(node, out var variable)) return new IdentifierExpr(variable.tok, variable); + return base.VisitExistsExpr(node); + } + + public override Expr VisitForallExpr(ForallExpr node) { + if (GetReplacementVariable(node, out var variable)) return new IdentifierExpr(variable.tok, variable); + return base.VisitForallExpr(node); + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) { + if (GetReplacementVariable(node, out var variable)) return new IdentifierExpr(variable.tok, variable); + return base.VisitIdentifierExpr(node); + } + + public override Expr VisitLetExpr(LetExpr node) { + if (GetReplacementVariable(node, out var variable)) return new IdentifierExpr(variable.tok, variable); + return base.VisitLetExpr(node); + } + + public override Expr VisitLiteralExpr(LiteralExpr node) { + if (GetReplacementVariable(node, out var variable)) return new IdentifierExpr(variable.tok, variable); + return base.VisitLiteralExpr(node); + } + + public override Expr VisitNAryExpr(NAryExpr node) { + if (GetReplacementVariable(node, out var variable)) return new IdentifierExpr(variable.tok, variable); + return base.VisitNAryExpr(node); + } + + public override Expr VisitOldExpr(OldExpr node) { + if (GetReplacementVariable(node, out var variable)) return new IdentifierExpr(variable.tok, variable); + return base.VisitOldExpr(node); + } + } +} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/LambdaLiftingTemplate.cs boogie-2.4.1+dfsg/Source/Core/LambdaLiftingTemplate.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/LambdaLiftingTemplate.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/LambdaLiftingTemplate.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie { + + /// + /// Templates are used in the lambda lifting algorithm based on maximally large subexpressions not containing + /// a lambda's bound variables (see ). + /// After the first phase of lambda lifting has been performed, each subexpression of a lambda is mapped to + /// a template. A template is an expression that can have "holes" as subexpressions (holes are the maximally + /// large subexpressions of a lambda that are free of bound variables and that will need to be lifted out of the + /// lambda). + /// Each template comes with a list of replacement expressions that indicate what each hole should be filled with + /// (in other words, what is the original expression that is now replaced by a hole). + /// + /// We represent templates as follows: + /// + /// + /// No bound variables (hole) + /// If a node does not have any bound variables, it is a hole. + /// This is represented using . + /// A hole has exactly one replacement expression. + /// + /// + /// Contains bound variables (template with holes) + /// If a node contains bound variables then it can have a (possibly empty, arbitrary-sized) list + /// of replacement expressions that indicate what the maximally large, bound-variable-free subexpressions + /// of the template are. + /// This is represented using . + /// + /// + /// No bound variables special case: trigger or attribute + /// Since triggers and attributes are not considered expressions in the AST, but are instead + /// represented as linked lists that contain sequences of expressions, we must allow triggers and attributes + /// to contain a replacement for each expression sequence. We therefore define a special class + /// that represents holes that can have multiple + /// replacements. + /// + /// + /// + public abstract class LambdaLiftingTemplate { + + protected readonly List llReplacements; + + protected LambdaLiftingTemplate(List llReplacements) { + this.llReplacements = new List(llReplacements); + } + + public List GetReplacements() { + return llReplacements; + } + + public abstract bool ContainsBoundVariables(); + } + + public class TemplateWithBoundVariables : LambdaLiftingTemplate { + + public TemplateWithBoundVariables(List llReplacements) : base(llReplacements) {} + + public TemplateWithBoundVariables() : base(new List()) { } // creating empty list so that we don't have to do null checks when concatenating lists + + public override bool ContainsBoundVariables() { + return true; + } + } + + public class TemplateNoBoundVariables : LambdaLiftingTemplate { + + public TemplateNoBoundVariables(Expr llReplacement) : base(new List() { llReplacement }) {} + + public Expr GetReplacement() { + Contract.Requires(llReplacements.Count == 1); + return llReplacements[0]; + } + + public override bool ContainsBoundVariables() { + return false; + } + } + + public class TemplateNoBoundVariablesTriggerOrKv : LambdaLiftingTemplate { + + public TemplateNoBoundVariablesTriggerOrKv(List llReplacements) : base(llReplacements) {} + + public override bool ContainsBoundVariables() { + return false; + } + } +} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/MaxHolesLambdaLifter.cs boogie-2.4.1+dfsg/Source/Core/MaxHolesLambdaLifter.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/MaxHolesLambdaLifter.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/MaxHolesLambdaLifter.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,395 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.IO; +using System.Linq; +using Microsoft.Boogie; +using Type = Microsoft.Boogie.Type; + +namespace Core { +using Set = GSet; + +/// +/// This visitor performs the first phase of the MaxHoles lambda lifting (see ), +/// after which it invokes the second phase in the method. +/// This phase consists of generating a "template" for each subexpression of the lambda. A template is an expression +/// that contains "holes", which will be the maximally large subexpressions that do not contain the lambda's bound +/// variables. +/// +/// To encode templates we store a dictionary that maps each node to its template, +/// an object of type . +/// +/// The first phase traverses a lambda's AST bottom-up, pupulating the map. +/// At the end of the first phase, we obtain a list of replacement expressions that correspond to the holes in +/// the lambda. +/// +/// The second phase replaces the templates with bound variables. It is handled by the +/// method. +/// +/// This class requires a lambda's `old` expressions to occur only around free variables (this +/// transformation is performed in LambdaHelper.LiftLambdaMaxHoles). +/// +class MaxHolesLambdaLifter : StandardVisitor { + private readonly List _nestedBoundVariables = new List(); + + private readonly LambdaExpr _lambda; + private readonly Dictionary _liftedLambdas; + private readonly String _freshFnName; + private readonly List _lambdaFunctions; + private readonly List _lambdaAxioms; + private int _freshVarCount; + + private readonly Dictionary _templates = new Dictionary(); + + public MaxHolesLambdaLifter( + LambdaExpr lambda, + Dictionary liftedLambdas, + string freshFnName, + List lambdaFunctions, + List lambdaAxioms, + int freshVarCount = 0 + ) { + _lambda = lambda; + _liftedLambdas = liftedLambdas; + _freshFnName = freshFnName; + _lambdaFunctions = lambdaFunctions; + _lambdaAxioms = lambdaAxioms; + _freshVarCount = freshVarCount; + } + + private string FreshVarNameGen(List existing) { + var prefix = "l#"; + while (true) { + var newName = prefix + _freshVarCount; + _freshVarCount++; + if (!existing.Contains(newName)) return newName; + } + } + + private bool IsBound(Variable node) { + return _lambda.Dummies.Contains(node) || _nestedBoundVariables.Contains(node); + } + + public override Expr VisitNAryExpr(NAryExpr node) { + Contract.Requires(node != null); + if (_templates.ContainsKey(node)) return node; + base.VisitNAryExpr(node); + var nodeArgs = node.Args; + if (nodeArgs.Any(arg => _templates[arg].ContainsBoundVariables())) { + var replacements = new List(); + foreach (Expr arg in nodeArgs) { + replacements.AddRange(_templates[arg].GetReplacements()); + } + + _templates[node] = new TemplateWithBoundVariables(replacements); + } else { + var newArgs = from arg in nodeArgs select ((TemplateNoBoundVariables) _templates[arg]).GetReplacement(); + var llReplacementExpr = + new NAryExpr(node.tok, node.Fun, newArgs.ToList(), node.Immutable) { + TypeParameters = node.TypeParameters + }; + llReplacementExpr.Type = node.Type ?? node.ShallowType; + _templates[node] = new TemplateNoBoundVariables(llReplacementExpr); + } + + return node; + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) { + Contract.Requires(node != null); + if (_templates.ContainsKey(node)) return node; + base.VisitIdentifierExpr(node); + if (IsBound(node.Decl)) { + _templates[node] = new TemplateWithBoundVariables(); + } else { + _templates[node] = _templates[node.Decl]; + } + + return node; + } + + public override Expr VisitLiteralExpr(LiteralExpr node) { + Contract.Requires(node != null); + if (_templates.ContainsKey(node)) return node; + base.VisitLiteralExpr(node); + _templates[node] = new TemplateNoBoundVariables(node); + return node; + } + + public override Expr VisitLetExpr(LetExpr node) { + Contract.Requires(node != null); + if (_templates.ContainsKey(node)) return node; + _nestedBoundVariables.AddRange(node.Dummies); + base.VisitLetExpr(node); + var bodyTemplate = _templates[node.Body]; + var varBodies = node.Rhss; + if (bodyTemplate.ContainsBoundVariables() || varBodies.Any(body => _templates[body].ContainsBoundVariables())) { + var replacements = new List(); + foreach (Expr body in varBodies) { + replacements.AddRange(_templates[body].GetReplacements()); + } + + replacements.AddRange(bodyTemplate.GetReplacements()); + _templates[node] = new TemplateWithBoundVariables(replacements); + } + else { + var newRhss = from arg in varBodies select ((TemplateNoBoundVariables) _templates[arg]).GetReplacement(); + LambdaLiftingTemplate template = new TemplateNoBoundVariables( + new LetExpr(node.tok, node.Dummies, newRhss.ToList(), + node.Attributes, + ((TemplateNoBoundVariables) _templates[node.Body]) + .GetReplacement()) {Type = node.Type}); + _templates[node] = template; + } + + return node; + } + + public override Expr VisitForallExpr(ForallExpr node) { + Contract.Requires(node != null); + if (_templates.ContainsKey(node)) return node; + _nestedBoundVariables.AddRange(node.Dummies); + base.VisitForallExpr(node); + var body = node.Body; + var bodyTemplate = _templates[body]; + var trigger = node.Triggers; + var triggerNoBounds = trigger == null || !_templates[trigger].ContainsBoundVariables(); + if (bodyTemplate is TemplateNoBoundVariables bt && triggerNoBounds) { + var newBody = bt.GetReplacement(); + var newTrigger = ReplacementTrigger(trigger); + _templates[node] = new TemplateNoBoundVariables( + new ForallExpr(node.tok, node.TypeParameters, node.Dummies, node.Attributes, newTrigger, + newBody, node.Immutable) {Type = node.Type}); + } else { + var replacements = bodyTemplate.GetReplacements(); + if (trigger != null) { + replacements.AddRange(_templates[trigger].GetReplacements()); + } + + _templates[node] = new TemplateWithBoundVariables(replacements); + } + + return node; + } + + public override Expr VisitExistsExpr(ExistsExpr node) { + Contract.Requires(node != null); + if (_templates.ContainsKey(node)) return node; + _nestedBoundVariables.AddRange(node.Dummies); + base.VisitExistsExpr(node); + var body = node.Body; + var bodyTemplate = _templates[body]; + var trigger = node.Triggers; + var triggerNoBounds = trigger == null || !_templates[trigger].ContainsBoundVariables(); + if (bodyTemplate is TemplateNoBoundVariables bt && triggerNoBounds) { + var newBody = bt.GetReplacement(); + var newTrigger = ReplacementTrigger(trigger); + _templates[node] = new TemplateNoBoundVariables( + new ExistsExpr(node.tok, node.TypeParameters, node.Dummies, node.Attributes, newTrigger, newBody, + node.Immutable) {Type = node.Type}); + } else { + var replacements = bodyTemplate.GetReplacements(); + if (trigger != null) { + replacements.AddRange(_templates[trigger].GetReplacements()); + } + + _templates[node] = new TemplateWithBoundVariables(replacements); + } + + return node; + } + + private List QKeyValueReplacements(QKeyValue node) { + Contract.Requires(node != null); + var ts = (from e in node.Params where e != null && e is Expr select _templates[e as Expr]).ToList(); + var replacements = new List(); + foreach (LambdaLiftingTemplate llt in ts) { + replacements.AddRange(llt.GetReplacements()); + } + + replacements.AddRange(node.Next == null ? new List() : QKeyValueReplacements(node.Next)); + return replacements; + } + + private Trigger ReplacementTrigger(Trigger trigger) { + if (trigger == null) return null; + var replacements = _templates[trigger].GetReplacements(); + var next = trigger.Next; + return new Trigger(trigger.tok, trigger.Pos, replacements, next == null ? null : ReplacementTrigger(next)); + } + + public override Trigger VisitTrigger(Trigger node) { + Contract.Requires(node != null); + if (_templates.ContainsKey(node)) return node; + base.VisitTrigger(node); + var templates = (from e in node.Tr select _templates[e]).ToList(); + var replacements = new List(); + if (node.Next != null) { + // the replacements for .Next must be added first, since the standard visitor visits them first + replacements.AddRange(_templates[node.Next].GetReplacements()); + } + foreach (LambdaLiftingTemplate llt in templates) { + replacements.AddRange(llt.GetReplacements()); + } + + var nextNoBounds = node.Next == null || !_templates[node.Next].ContainsBoundVariables(); + if (nextNoBounds && templates.All(r => !r.ContainsBoundVariables())) { + _templates[node] = new TemplateNoBoundVariablesTriggerOrKv(replacements); + } else { + _templates[node] = new TemplateWithBoundVariables(replacements); + } + + return node; + } + + public override Expr VisitLambdaExpr(LambdaExpr node) { + // Phase I of the lambda lifting + base.VisitLambdaExpr(node); + if (node.Attributes != null) { + VisitQKeyValue(node.Attributes); + } + + // Phase II of the lambda lifting. + var attribReplacementExprs = + node.Attributes == null ? new List() : QKeyValueReplacements(node.Attributes); + var llReplacementExprs = _templates[node.Body].GetReplacements(); + var allReplacementExprs = new List(attribReplacementExprs); + allReplacementExprs.AddRange(llReplacementExprs); + var typedIdents = (from replExpr in allReplacementExprs + select new TypedIdent(replExpr.tok, FreshVarNameGen(new List()), + replExpr.Type ?? replExpr.ShallowType)) + .ToList(); + var formals = allReplacementExprs.Zip(typedIdents, + (replExpr, typedIdent) => (Variable) new Formal(replExpr.tok, typedIdent, true)); + var replDummies = allReplacementExprs.Zip(typedIdents, + (replExpr, typedIdent) => (Variable) new BoundVariable(replExpr.tok, typedIdent)).ToList(); + var replDummyIds = (from dummy in replDummies select (Expr) new IdentifierExpr(dummy.tok, dummy)).ToList(); + var dummies = new List(_lambda.Dummies); + dummies.AddRange(replDummies); + + + var lambdaAttrs = _lambda.Attributes; + if (0 < CommandLineOptions.Clo.VerifySnapshots && QKeyValue.FindStringAttribute(lambdaAttrs, "checksum") == null) { + // Attach a dummy checksum to avoid issues in the dependency analysis. + var checksumAttr = new QKeyValue(_lambda.tok, "checksum", new List { "lambda expression" }, null); + if (lambdaAttrs == null) { + lambdaAttrs = checksumAttr; + } else { + lambdaAttrs.AddLast(checksumAttr); + } + } + + Set freeVars = new Set(); + BinderExpr.ComputeBinderFreeVariables(_lambda.TypeParameters, _lambda.Dummies, _lambda.Body, null, lambdaAttrs, + freeVars); + var freeTypeVars = freeVars.OfType().ToList(); + var freeVarActuals = freeVars.OfType().ToList(); + + var sw = new StringWriter(); + var wr = new TokenTextWriter(sw, true); + _lambda.Emit(wr); + string lam_str = sw.ToString(); + + // the resulting lifted function applied to free variables + FunctionCall fcall; + IToken tok = _lambda.tok; + Formal res = new Formal(tok, new TypedIdent(tok, TypedIdent.NoName, cce.NonNull(_lambda.Type)), false); + + var liftedLambda = (LambdaExpr) LambdaLiftingMaxHolesFiller.Fill( + (from kvp in _templates where !kvp.Value.ContainsBoundVariables() select kvp.Key).ToList(), + replDummies, _lambda, lambdaAttrs); + + if (_liftedLambdas.TryGetValue(liftedLambda, out fcall)) { + if (CommandLineOptions.Clo.TraceVerify) { + Console.WriteLine("Old lambda: {0}", lam_str); + } + } + else { + if (CommandLineOptions.Clo.TraceVerify) { + Console.WriteLine("New lambda: {0}", lam_str); + } + + var freshTypeVars = (from tv in freeTypeVars select new TypeVariable(tv.tok, tv.Name)).ToList(); + // this will be the lifted function that takes free variables as arguments + Function fn = new Function(tok, _freshFnName, freshTypeVars, formals.ToList(), res, + "auto-generated lambda function", liftedLambda.Attributes) {OriginalLambdaExprAsString = lam_str}; + + fcall = new FunctionCall(new IdentifierExpr(tok, fn.Name)); + fcall.Func = fn; + _liftedLambdas[liftedLambda] = fcall; + + // the arguments to the select map (the map that will be equal to the lifted lambda) + List selectArgs = new List(); + foreach (Variable v in _lambda.Dummies) { + Contract.Assert(v != null); + selectArgs.Add(new IdentifierExpr(v.tok, v)); + } + + NAryExpr axcall = new NAryExpr(tok, fcall, replDummyIds) { + Type = res.TypedIdent.Type, + TypeParameters = SimpleTypeParamInstantiation.From(freeTypeVars, freeVarActuals) + }; + // the Boogie map, applied to selectArgs, will be equal to the lifted lambda call + NAryExpr select = Expr.Select(axcall, selectArgs); + select.Type = _lambda.Body.Type; + List selectTypeParamActuals = new List(); + List forallTypeVariables = new List(); + foreach (TypeVariable tp in _lambda.TypeParameters) { + Contract.Assert(tp != null); + selectTypeParamActuals.Add(tp); + forallTypeVariables.Add(tp); + } + + forallTypeVariables.AddRange(freeTypeVars); + select.TypeParameters = SimpleTypeParamInstantiation.From(_lambda.TypeParameters, selectTypeParamActuals); + + + NAryExpr body = Expr.Eq(select, liftedLambda.Body); + body.Type = Type.Bool; + body.TypeParameters = SimpleTypeParamInstantiation.EMPTY; + var trig = new Trigger(select.tok, true, new List {select}); + + _lambdaFunctions.Add(fn); + _lambdaAxioms.Add(new ForallExpr(tok, forallTypeVariables, dummies, liftedLambda.Attributes, trig, body)); + } + + NAryExpr call = new NAryExpr(tok, fcall, allReplacementExprs.ToList()) { + Type = res.TypedIdent.Type, TypeParameters = SimpleTypeParamInstantiation.From(freeTypeVars, freeVarActuals) + }; + + return call; + } + + public override Variable VisitVariable(Variable node) { + Contract.Requires(node != null); + if (_templates.ContainsKey(node)) return node; + base.VisitVariable(node); + if (IsBound(node)) { + _templates[node] = new TemplateWithBoundVariables(); + } else { + var identifierExpr = new IdentifierExpr(node.tok, node); + identifierExpr.Type = node.TypedIdent.Type; + _templates[node] = new TemplateNoBoundVariables(identifierExpr); + } + + return node; + } + + public override Expr VisitOldExpr(OldExpr node) { + Contract.Requires(node != null); + if (_templates.ContainsKey(node)) return node; + base.VisitOldExpr(node); + if (_templates[node.Expr] is TemplateNoBoundVariables t) { + var replacement = t.GetReplacement(); + var llReplacement = new OldExpr(replacement.tok, replacement) {Type = replacement.Type}; + _templates[node] = new TemplateNoBoundVariables(llReplacement); + } + else { + // After OldFinder pass, old expressions should only occur around free variables + throw new cce.UnreachableException(); + } + + return node; + } +} +} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Parser.cs boogie-2.4.1+dfsg/Source/Core/Parser.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Parser.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/Parser.cs 2019-10-25 18:17:05.000000000 +0000 @@ -25,10 +25,10 @@ public const int _decimal = 5; public const int _dec_float = 6; public const int _float = 7; - public const int maxT = 97; + public const int maxT = 107; - const bool T = true; - const bool x = false; + const bool _T = true; + const bool _x = false; const int minErrDist = 2; public Scanner/*!*/ scanner; @@ -84,17 +84,18 @@ Parser parser = new Parser(scanner, errors, false); parser.Parse(); - if (parser.errors.count == 0) + if (errors.count == 0) { - program = parser.Pgm; - program.ProcessDatatypeConstructors(); - return 0; + parser.Pgm.ProcessDatatypeConstructors(errors); } - else + if (errors.count == 0) + { + program = parser.Pgm; + } else { program = null; - return parser.errors.count; } + return errors.count; } public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors, bool disambiguation) @@ -367,7 +368,7 @@ Get(); Type(out retTy); retTyd = new TypedIdent(retTy.tok, TypedIdent.NoName, retTy); - } else SynErr(98); + } else SynErr(108); if (la.kind == 28) { Get(); Expression(out tmp); @@ -375,7 +376,7 @@ Expect(29); } else if (la.kind == 9) { Get(); - } else SynErr(99); + } else SynErr(109); if (retTyd == null) { // construct a dummy type for the case of syntax error retTyd = new TypedIdent(t, TypedIdent.NoName, new BasicType(t, SimpleType.Int)); @@ -498,7 +499,7 @@ impl = new Implementation(x, x.val, typeParams, Formal.StripWhereClauses(ins), Formal.StripWhereClauses(outs), locals, stmtList, kv == null ? null : (QKeyValue)kv.Clone(), this.errors); - } else SynErr(100); + } else SynErr(110); proc = new Procedure(x, x.val, typeParams, ins, outs, pre, mods, post, kv); } @@ -563,8 +564,7 @@ } } - void BoundVars(IToken/*!*/ x, out List/*!*/ ds) { - Contract.Requires(x != null); + void BoundVars(out List/*!*/ ds) { Contract.Ensures(Contract.ValueAtReturn(out ds) != null); List/*!*/ tyds = new List(); ds = new List(); @@ -610,7 +610,7 @@ ty = new UnresolvedTypeIdentifier (tok, tok.val, args); } else if (la.kind == 18 || la.kind == 20) { MapType(out ty); - } else SynErr(101); + } else SynErr(111); } void AttributesIdsTypeWhere(bool allowAttributes, bool allowWhereClauses, string context, System.Action action ) { @@ -674,7 +674,7 @@ Get(); Type(out ty); Expect(11); - } else SynErr(102); + } else SynErr(112); } void Ident(out IToken/*!*/ x) { @@ -704,7 +704,7 @@ } else if (la.kind == 18 || la.kind == 20) { MapType(out ty); ts.Add(ty); - } else SynErr(103); + } else SynErr(113); } void MapType(out Bpl.Type/*!*/ ty) { @@ -889,7 +889,7 @@ SpecPrePost(true, pre, post); } else if (la.kind == 37 || la.kind == 38) { SpecPrePost(false, pre, post); - } else SynErr(104); + } else SynErr(114); } void ImplBody(out List/*!*/ locals, out StmtList/*!*/ stmtList) { @@ -921,7 +921,7 @@ Proposition(out e); Expect(9); post.Add(new Ensures(tok, free, e, null, kv)); - } else SynErr(105); + } else SynErr(115); } void StmtList(out StmtList/*!*/ stmtList) { @@ -1060,7 +1060,7 @@ c = new YieldCmd(x); break; } - default: SynErr(106); break; + default: SynErr(116); break; } } @@ -1077,7 +1077,7 @@ } else if (la.kind == 46) { BreakCmd(out bcmd); ec = bcmd; - } else SynErr(107); + } else SynErr(117); } void TransferCmd(out TransferCmd/*!*/ tc) { @@ -1097,7 +1097,7 @@ } else if (la.kind == 40) { Get(); tc = new ReturnCmd(t); - } else SynErr(108); + } else SynErr(118); Expect(9); } @@ -1122,7 +1122,7 @@ Get(); StmtList(out els); elseOption = els; - } else SynErr(109); + } else SynErr(119); } ifcmd = new IfCmd(x, guard, thn, elseIfOption, elseOption); } @@ -1186,7 +1186,7 @@ } else if (StartOf(9)) { Expression(out ee); e = ee; - } else SynErr(110); + } else SynErr(120); Expect(11); } @@ -1197,6 +1197,7 @@ List/*!*/ lhss; List/*!*/ rhss; List/*!*/ indexes; + QKeyValue kv = null; Ident(out id); x = t; @@ -1223,6 +1224,9 @@ } Expect(51); x = t; /* use location of := */ + while (la.kind == 28) { + Attribute(ref kv); + } Expression(out e0); rhss = new List (); rhss.Add(e0); @@ -1232,8 +1236,8 @@ rhss.Add(e0); } Expect(9); - c = new AssignCmd(x, lhss, rhss); - } else SynErr(111); + c = new AssignCmd(x, lhss, rhss, kv); + } else SynErr(121); } void CallCmd(out Cmd c) { @@ -1350,7 +1354,7 @@ } Expect(11); c = new CallCmd(x, first.val, es, ids, kv); ((CallCmd) c).IsFree = isFree; ((CallCmd) c).IsAsync = isAsync; - } else SynErr(112); + } else SynErr(122); } void Expressions(out List/*!*/ es) { @@ -1395,7 +1399,7 @@ Get(); } else if (la.kind == 57) { Get(); - } else SynErr(113); + } else SynErr(123); } void LogicalExpression(out Expr/*!*/ e0) { @@ -1433,7 +1437,7 @@ Get(); } else if (la.kind == 59) { Get(); - } else SynErr(114); + } else SynErr(124); } void ExpliesOp() { @@ -1441,7 +1445,7 @@ Get(); } else if (la.kind == 61) { Get(); - } else SynErr(115); + } else SynErr(125); } void RelationalExpression(out Expr/*!*/ e0) { @@ -1459,7 +1463,7 @@ Get(); } else if (la.kind == 63) { Get(); - } else SynErr(116); + } else SynErr(126); } void OrOp() { @@ -1467,7 +1471,7 @@ Get(); } else if (la.kind == 65) { Get(); - } else SynErr(117); + } else SynErr(127); } void BvTerm(out Expr/*!*/ e0) { @@ -1534,7 +1538,7 @@ x = t; op=BinaryOperator.Opcode.Ge; break; } - default: SynErr(118); break; + default: SynErr(128); break; } } @@ -1566,7 +1570,7 @@ } else if (la.kind == 76) { Get(); x = t; op=BinaryOperator.Opcode.Sub; - } else SynErr(119); + } else SynErr(129); } void Power(out Expr/*!*/ e0) { @@ -1594,7 +1598,7 @@ } else if (la.kind == 79) { Get(); x = t; op=BinaryOperator.Opcode.RealDiv; - } else SynErr(120); + } else SynErr(130); } void UnaryExpression(out Expr/*!*/ e) { @@ -1613,7 +1617,7 @@ e = Expr.Unary(x, UnaryOperator.Opcode.Not, e); } else if (StartOf(14)) { CoercionExpression(out e); - } else SynErr(121); + } else SynErr(131); } void NegOp() { @@ -1621,7 +1625,7 @@ Get(); } else if (la.kind == 82) { Get(); - } else SynErr(122); + } else SynErr(132); } void CoercionExpression(out Expr/*!*/ e) { @@ -1645,7 +1649,7 @@ e = new BvBounds(x, bn, ((LiteralExpr)e).asBigNum); } - } else SynErr(123); + } else SynErr(133); } } @@ -1736,6 +1740,51 @@ e = new LiteralExpr(t, true); break; } + case 85: case 86: { + if (la.kind == 85) { + Get(); + } else { + Get(); + } + e = new LiteralExpr(t, RoundingMode.RNE); + break; + } + case 87: case 88: { + if (la.kind == 87) { + Get(); + } else { + Get(); + } + e = new LiteralExpr(t, RoundingMode.RNA); + break; + } + case 89: case 90: { + if (la.kind == 89) { + Get(); + } else { + Get(); + } + e = new LiteralExpr(t, RoundingMode.RTP); + break; + } + case 91: case 92: { + if (la.kind == 91) { + Get(); + } else { + Get(); + } + e = new LiteralExpr(t, RoundingMode.RTN); + break; + } + case 93: case 94: { + if (la.kind == 93) { + Get(); + } else { + Get(); + } + e = new LiteralExpr(t, RoundingMode.RTZ); + break; + } case 3: { Nat(out bn); e = new LiteralExpr(t, bn); @@ -1766,12 +1815,12 @@ e = new NAryExpr(x, new FunctionCall(id), es); } else if (la.kind == 11) { e = new NAryExpr(x, new FunctionCall(id), new List()); - } else SynErr(124); + } else SynErr(134); Expect(11); } break; } - case 85: { + case 95: { Get(); x = t; Expect(10); @@ -1805,19 +1854,19 @@ if (e is BvBounds) this.SemErr("parentheses around bitvector bounds " + "are not allowed"); - } else if (la.kind == 89 || la.kind == 90) { + } else if (la.kind == 99 || la.kind == 100) { Forall(); x = t; QuantifierBody(x, out typeParams, out ds, out kv, out trig, out e); if (typeParams.Count + ds.Count > 0) e = new ForallExpr(x, typeParams, ds, kv, trig, e); - } else if (la.kind == 91 || la.kind == 92) { + } else if (la.kind == 101 || la.kind == 102) { Exists(); x = t; QuantifierBody(x, out typeParams, out ds, out kv, out trig, out e); if (typeParams.Count + ds.Count > 0) e = new ExistsExpr(x, typeParams, ds, kv, trig, e); - } else if (la.kind == 93 || la.kind == 94) { + } else if (la.kind == 103 || la.kind == 104) { Lambda(); x = t; QuantifierBody(x, out typeParams, out ds, out kv, out trig, out e); @@ -1825,7 +1874,9 @@ SemErr("triggers not allowed in lambda expressions"); if (typeParams.Count + ds.Count > 0) e = new LambdaExpr(x, typeParams, ds, kv, e); - } else SynErr(125); + } else if (la.kind == 8) { + LetExpr(out e); + } else SynErr(135); Expect(11); break; } @@ -1833,12 +1884,12 @@ IfThenElseExpression(out e); break; } - case 86: { + case 96: { CodeExpression(out locals, out blocks); e = new CodeExpr(locals, blocks); break; } - default: SynErr(126); break; + default: SynErr(136); break; } } @@ -1850,7 +1901,7 @@ } else if (la.kind == 6) { Get(); s = t.val; - } else SynErr(127); + } else SynErr(137); try { n = BigDec.FromString(s); } catch (FormatException) { @@ -1890,11 +1941,11 @@ } void Forall() { - if (la.kind == 89) { + if (la.kind == 99) { Get(); - } else if (la.kind == 90) { + } else if (la.kind == 100) { Get(); - } else SynErr(128); + } else SynErr(138); } void QuantifierBody(IToken/*!*/ q, out List/*!*/ typeParams, out List/*!*/ ds, @@ -1908,11 +1959,11 @@ if (la.kind == 20) { TypeParams(out tok, out typeParams); if (la.kind == 1 || la.kind == 28) { - BoundVars(q, out ds); + BoundVars(out ds); } } else if (la.kind == 1 || la.kind == 28) { - BoundVars(q, out ds); - } else SynErr(129); + BoundVars(out ds); + } else SynErr(139); QSep(); while (la.kind == 28) { AttributeOrTrigger(ref kv, ref trig); @@ -1921,19 +1972,53 @@ } void Exists() { - if (la.kind == 91) { + if (la.kind == 101) { Get(); - } else if (la.kind == 92) { + } else if (la.kind == 102) { Get(); - } else SynErr(130); + } else SynErr(140); } void Lambda() { - if (la.kind == 93) { + if (la.kind == 103) { Get(); - } else if (la.kind == 94) { + } else if (la.kind == 104) { Get(); - } else SynErr(131); + } else SynErr(141); + } + + void LetExpr(out Expr/*!*/ letexpr) { + IToken tok; + Variable v; + var ds = new List(); + Expr e0; + var rhss = new List(); + QKeyValue kv = null; + Expr body; + + Expect(8); + tok = t; + LetVar(out v); + ds.Add(v); + while (la.kind == 13) { + Get(); + LetVar(out v); + ds.Add(v); + } + Expect(51); + Expression(out e0); + rhss.Add(e0); + while (la.kind == 13) { + Get(); + Expression(out e0); + rhss.Add(e0); + } + Expect(9); + while (la.kind == 28) { + Attribute(ref kv); + } + Expression(out body); + letexpr = new LetExpr(tok, ds, rhss, kv, body); } void IfThenElseExpression(out Expr/*!*/ e) { @@ -1944,7 +2029,7 @@ Expect(41); tok = t; Expression(out e0); - Expect(88); + Expect(98); Expression(out e1); Expect(42); Expression(out e2); @@ -1955,7 +2040,7 @@ Contract.Ensures(Contract.ValueAtReturn(out locals) != null); Contract.Ensures(cce.NonNullElements(Contract.ValueAtReturn(out blocks))); locals = new List(); Block/*!*/ b; blocks = new List(); - Expect(86); + Expect(96); while (la.kind == 8) { LocalVars(locals); } @@ -1965,7 +2050,7 @@ SpecBlock(out b); blocks.Add(b); } - Expect(87); + Expect(97); } void SpecBlock(out Block/*!*/ b) { @@ -2003,7 +2088,7 @@ Get(); Expression(out e); b = new Block(x,x.val,cs,new ReturnExprCmd(t,e)); - } else SynErr(132); + } else SynErr(142); Expect(9); } @@ -2060,7 +2145,7 @@ trig.AddLast(new Trigger(tok, true, es, null)); } - } else SynErr(133); + } else SynErr(143); Expect(29); } @@ -2075,15 +2160,28 @@ } else if (StartOf(9)) { Expression(out e); o = e; - } else SynErr(134); + } else SynErr(144); } void QSep() { - if (la.kind == 95) { + if (la.kind == 105) { Get(); - } else if (la.kind == 96) { + } else if (la.kind == 106) { Get(); - } else SynErr(135); + } else SynErr(145); + } + + void LetVar(out Variable/*!*/ v) { + QKeyValue kv = null; + IToken id; + + while (la.kind == 28) { + Attribute(ref kv); + } + Ident(out id); + var tyd = new TypedIdent(id, id.val, dummyType/*will be replaced during type checking*/, null); + v = new BoundVariable(tyd.tok, tyd, kv); + } @@ -2099,23 +2197,23 @@ } static readonly bool[,]/*!*/ set = { - {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,x,T,x, x,x,T,T, x,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,T, T,T,T,x, T,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,T, T,T,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,T, T,T,x,T, x,x,T,T, T,T,T,x, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,T, T,T,T,x, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,T,T, x,T,T,T, x,x,T,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,T,T, x,T,T,T, x,x,T,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,T,T, x,T,T,T, x,x,T,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,T,T, T,T,T,T, x,x,T,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x} + {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_T,_x, _x,_x,_T,_T, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _T,_T,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _T,_T,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_T, _T,_T,_x,_T, _x,_x,_T,_T, _T,_T,_T,_x, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_T,_T, _x,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_T,_T, _x,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_T,_T, _x,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_T,_T, _T,_T,_T,_T, _x,_x,_T,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x} }; } // end Parser @@ -2225,57 +2323,67 @@ case 82: s = "\"\\u00ac\" expected"; break; case 83: s = "\"false\" expected"; break; case 84: s = "\"true\" expected"; break; - case 85: s = "\"old\" expected"; break; - case 86: s = "\"|{\" expected"; break; - case 87: s = "\"}|\" expected"; break; - case 88: s = "\"then\" expected"; break; - case 89: s = "\"forall\" expected"; break; - case 90: s = "\"\\u2200\" expected"; break; - case 91: s = "\"exists\" expected"; break; - case 92: s = "\"\\u2203\" expected"; break; - case 93: s = "\"lambda\" expected"; break; - case 94: s = "\"\\u03bb\" expected"; break; - case 95: s = "\"::\" expected"; break; - case 96: s = "\"\\u2022\" expected"; break; - case 97: s = "??? expected"; break; - case 98: s = "invalid Function"; break; - case 99: s = "invalid Function"; break; - case 100: s = "invalid Procedure"; break; - case 101: s = "invalid Type"; break; - case 102: s = "invalid TypeAtom"; break; - case 103: s = "invalid TypeArgs"; break; - case 104: s = "invalid Spec"; break; - case 105: s = "invalid SpecPrePost"; break; - case 106: s = "invalid LabelOrCmd"; break; - case 107: s = "invalid StructuredCmd"; break; - case 108: s = "invalid TransferCmd"; break; - case 109: s = "invalid IfCmd"; break; - case 110: s = "invalid Guard"; break; - case 111: s = "invalid LabelOrAssign"; break; - case 112: s = "invalid CallParams"; break; - case 113: s = "invalid EquivOp"; break; - case 114: s = "invalid ImpliesOp"; break; - case 115: s = "invalid ExpliesOp"; break; - case 116: s = "invalid AndOp"; break; - case 117: s = "invalid OrOp"; break; - case 118: s = "invalid RelOp"; break; - case 119: s = "invalid AddOp"; break; - case 120: s = "invalid MulOp"; break; - case 121: s = "invalid UnaryExpression"; break; - case 122: s = "invalid NegOp"; break; - case 123: s = "invalid CoercionExpression"; break; - case 124: s = "invalid AtomExpression"; break; - case 125: s = "invalid AtomExpression"; break; - case 126: s = "invalid AtomExpression"; break; - case 127: s = "invalid Dec"; break; - case 128: s = "invalid Forall"; break; - case 129: s = "invalid QuantifierBody"; break; - case 130: s = "invalid Exists"; break; - case 131: s = "invalid Lambda"; break; - case 132: s = "invalid SpecBlock"; break; - case 133: s = "invalid AttributeOrTrigger"; break; - case 134: s = "invalid AttributeParameter"; break; - case 135: s = "invalid QSep"; break; + case 85: s = "\"roundNearestTiesToEven\" expected"; break; + case 86: s = "\"RNE\" expected"; break; + case 87: s = "\"roundNearestTiesToAway\" expected"; break; + case 88: s = "\"RNA\" expected"; break; + case 89: s = "\"roundTowardPositive\" expected"; break; + case 90: s = "\"RTP\" expected"; break; + case 91: s = "\"roundTowardNegative\" expected"; break; + case 92: s = "\"RTN\" expected"; break; + case 93: s = "\"roundTowardZero\" expected"; break; + case 94: s = "\"RTZ\" expected"; break; + case 95: s = "\"old\" expected"; break; + case 96: s = "\"|{\" expected"; break; + case 97: s = "\"}|\" expected"; break; + case 98: s = "\"then\" expected"; break; + case 99: s = "\"forall\" expected"; break; + case 100: s = "\"\\u2200\" expected"; break; + case 101: s = "\"exists\" expected"; break; + case 102: s = "\"\\u2203\" expected"; break; + case 103: s = "\"lambda\" expected"; break; + case 104: s = "\"\\u03bb\" expected"; break; + case 105: s = "\"::\" expected"; break; + case 106: s = "\"\\u2022\" expected"; break; + case 107: s = "??? expected"; break; + case 108: s = "invalid Function"; break; + case 109: s = "invalid Function"; break; + case 110: s = "invalid Procedure"; break; + case 111: s = "invalid Type"; break; + case 112: s = "invalid TypeAtom"; break; + case 113: s = "invalid TypeArgs"; break; + case 114: s = "invalid Spec"; break; + case 115: s = "invalid SpecPrePost"; break; + case 116: s = "invalid LabelOrCmd"; break; + case 117: s = "invalid StructuredCmd"; break; + case 118: s = "invalid TransferCmd"; break; + case 119: s = "invalid IfCmd"; break; + case 120: s = "invalid Guard"; break; + case 121: s = "invalid LabelOrAssign"; break; + case 122: s = "invalid CallParams"; break; + case 123: s = "invalid EquivOp"; break; + case 124: s = "invalid ImpliesOp"; break; + case 125: s = "invalid ExpliesOp"; break; + case 126: s = "invalid AndOp"; break; + case 127: s = "invalid OrOp"; break; + case 128: s = "invalid RelOp"; break; + case 129: s = "invalid AddOp"; break; + case 130: s = "invalid MulOp"; break; + case 131: s = "invalid UnaryExpression"; break; + case 132: s = "invalid NegOp"; break; + case 133: s = "invalid CoercionExpression"; break; + case 134: s = "invalid AtomExpression"; break; + case 135: s = "invalid AtomExpression"; break; + case 136: s = "invalid AtomExpression"; break; + case 137: s = "invalid Dec"; break; + case 138: s = "invalid Forall"; break; + case 139: s = "invalid QuantifierBody"; break; + case 140: s = "invalid Exists"; break; + case 141: s = "invalid Lambda"; break; + case 142: s = "invalid SpecBlock"; break; + case 143: s = "invalid AttributeOrTrigger"; break; + case 144: s = "invalid AttributeParameter"; break; + case 145: s = "invalid QSep"; break; default: s = "error " + n; break; } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/ResolutionContext.cs boogie-2.4.1+dfsg/Source/Core/ResolutionContext.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/ResolutionContext.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/ResolutionContext.cs 2019-10-25 18:17:05.000000000 +0000 @@ -33,6 +33,7 @@ public CheckingContext(IErrorSink errorSink) { this.errorSink = errorSink; + this.errors = 0; } public int ErrorCount { diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Scanner.cs boogie-2.4.1+dfsg/Source/Core/Scanner.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Scanner.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/Scanner.cs 2019-10-25 18:17:05.000000000 +0000 @@ -211,8 +211,8 @@ public class Scanner { const char EOL = '\n'; const int eofSym = 0; /* pdt */ - const int maxT = 97; - const int noSym = 97; + const int maxT = 107; + const int noSym = 107; [ContractInvariantMethod] @@ -267,42 +267,42 @@ for (int i = 65; i <= 90; ++i) start[i] = 2; for (int i = 94; i <= 122; ++i) start[i] = 2; for (int i = 126; i <= 126; ++i) start[i] = 2; - for (int i = 49; i <= 57; ++i) start[i] = 45; + for (int i = 49; i <= 57; ++i) start[i] = 51; for (int i = 34; i <= 34; ++i) start[i] = 6; start[92] = 1; - start[45] = 87; - start[48] = 46; - start[59] = 52; - start[40] = 53; - start[41] = 54; - start[58] = 88; - start[44] = 55; - start[91] = 56; - start[93] = 57; - start[60] = 89; - start[62] = 90; - start[123] = 58; - start[125] = 91; - start[61] = 92; - start[42] = 93; - start[124] = 94; - start[8660] = 61; - start[8658] = 63; - start[8656] = 64; - start[38] = 65; - start[8743] = 67; - start[8744] = 69; - start[33] = 95; - start[8800] = 73; - start[8804] = 74; - start[8805] = 75; - start[43] = 96; - start[47] = 77; - start[172] = 79; - start[8704] = 82; - start[8707] = 83; - start[955] = 84; - start[8226] = 86; + start[48] = 52; + start[45] = 96; + start[59] = 61; + start[40] = 62; + start[41] = 63; + start[58] = 97; + start[44] = 64; + start[91] = 65; + start[93] = 66; + start[60] = 98; + start[62] = 99; + start[123] = 67; + start[125] = 100; + start[61] = 101; + start[42] = 102; + start[124] = 103; + start[8660] = 70; + start[8658] = 72; + start[8656] = 73; + start[38] = 74; + start[8743] = 76; + start[8744] = 78; + start[33] = 104; + start[8800] = 82; + start[8804] = 83; + start[8805] = 84; + start[43] = 105; + start[47] = 86; + start[172] = 88; + start[8704] = 91; + start[8707] = 92; + start[955] = 93; + start[8226] = 95; start[Buffer.EOF] = -1; } @@ -541,11 +541,21 @@ case "mod": t.kind = 78; break; case "false": t.kind = 83; break; case "true": t.kind = 84; break; - case "old": t.kind = 85; break; - case "then": t.kind = 88; break; - case "forall": t.kind = 89; break; - case "exists": t.kind = 91; break; - case "lambda": t.kind = 93; break; + case "roundNearestTiesToEven": t.kind = 85; break; + case "RNE": t.kind = 86; break; + case "roundNearestTiesToAway": t.kind = 87; break; + case "RNA": t.kind = 88; break; + case "roundTowardPositive": t.kind = 89; break; + case "RTP": t.kind = 90; break; + case "roundTowardNegative": t.kind = 91; break; + case "RTN": t.kind = 92; break; + case "roundTowardZero": t.kind = 93; break; + case "RTZ": t.kind = 94; break; + case "old": t.kind = 95; break; + case "then": t.kind = 98; break; + case "forall": t.kind = 99; break; + case "exists": t.kind = 101; break; + case "lambda": t.kind = 103; break; default: break; } } @@ -598,63 +608,63 @@ case 6: if (ch == '"') {AddCh(); goto case 7;} else if (ch <= 9 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= '!' || ch >= '#' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 6;} - else if (ch == 92) {AddCh(); goto case 47;} + else if (ch == 92) {AddCh(); goto case 53;} else {goto case 0;} case 7: {t.kind = 4; break;} case 8: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 9;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 10;} + else if (ch == '-') {AddCh(); goto case 9;} else {goto case 0;} case 9: - recEnd = pos; recKind = 6; - if (ch >= '0' && ch <= '9') {AddCh(); goto case 9;} - else if (ch == 'e') {AddCh(); goto case 10;} - else {t.kind = 6; break;} - case 10: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 12;} - else if (ch == '-') {AddCh(); goto case 11;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 10;} else {goto case 0;} + case 10: + recEnd = pos; recKind = 5; + if (ch >= '0' && ch <= '9') {AddCh(); goto case 10;} + else {t.kind = 5; break;} case 11: if (ch >= '0' && ch <= '9') {AddCh(); goto case 12;} else {goto case 0;} case 12: recEnd = pos; recKind = 6; if (ch >= '0' && ch <= '9') {AddCh(); goto case 12;} + else if (ch == 'e') {AddCh(); goto case 13;} else {t.kind = 6; break;} case 13: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 13;} - else if (ch == 'e') {AddCh(); goto case 14;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 15;} + else if (ch == '-') {AddCh(); goto case 14;} else {goto case 0;} case 14: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 16;} - else if (ch == '-') {AddCh(); goto case 15;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 15;} else {goto case 0;} case 15: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 16;} - else {goto case 0;} + recEnd = pos; recKind = 6; + if (ch >= '0' && ch <= '9') {AddCh(); goto case 15;} + else {t.kind = 6; break;} case 16: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 16;} - else if (ch == 'f') {AddCh(); goto case 17;} + if (ch == 'x') {AddCh(); goto case 17;} else {goto case 0;} case 17: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 18;} + if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {AddCh(); goto case 18;} else {goto case 0;} case 18: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 18;} - else if (ch == 'e') {AddCh(); goto case 19;} + if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {AddCh(); goto case 18;} + else if (ch == '.') {AddCh(); goto case 19;} else {goto case 0;} case 19: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 20;} + if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {AddCh(); goto case 20;} else {goto case 0;} case 20: - recEnd = pos; recKind = 7; - if (ch >= '0' && ch <= '9') {AddCh(); goto case 20;} - else {t.kind = 7; break;} + if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'd' || ch == 'f') {AddCh(); goto case 20;} + else if (ch == 'e') {AddCh(); goto case 54;} + else {goto case 0;} case 21: - if (ch == 'a') {AddCh(); goto case 22;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 22;} else {goto case 0;} case 22: - if (ch == 'N') {AddCh(); goto case 23;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 22;} + else if (ch == 'f') {AddCh(); goto case 23;} else {goto case 0;} case 23: if (ch >= '0' && ch <= '9') {AddCh(); goto case 24;} @@ -674,7 +684,7 @@ if (ch == 'a') {AddCh(); goto case 28;} else {goto case 0;} case 28: - if (ch == 'n') {AddCh(); goto case 29;} + if (ch == 'N') {AddCh(); goto case 29;} else {goto case 0;} case 29: if (ch >= '0' && ch <= '9') {AddCh(); goto case 30;} @@ -691,10 +701,10 @@ if (ch >= '0' && ch <= '9') {AddCh(); goto case 32;} else {t.kind = 7; break;} case 33: - if (ch == 'o') {AddCh(); goto case 34;} + if (ch == 'a') {AddCh(); goto case 34;} else {goto case 0;} case 34: - if (ch == 'o') {AddCh(); goto case 35;} + if (ch == 'n') {AddCh(); goto case 35;} else {goto case 0;} case 35: if (ch >= '0' && ch <= '9') {AddCh(); goto case 36;} @@ -731,171 +741,215 @@ if (ch >= '0' && ch <= '9') {AddCh(); goto case 44;} else {t.kind = 7; break;} case 45: - recEnd = pos; recKind = 3; - if (ch >= '0' && ch <= '9') {AddCh(); goto case 45;} - else if (ch == 'b') {AddCh(); goto case 3;} - else if (ch == 'e') {AddCh(); goto case 48;} - else if (ch == '.') {AddCh(); goto case 8;} - else {t.kind = 3; break;} + if (ch == 'o') {AddCh(); goto case 46;} + else {goto case 0;} case 46: - recEnd = pos; recKind = 3; - if (ch >= '0' && ch <= '9') {AddCh(); goto case 45;} - else if (ch == 'b') {AddCh(); goto case 3;} - else if (ch == 'e') {AddCh(); goto case 48;} - else if (ch == '.') {AddCh(); goto case 8;} - else if (ch == 'N') {AddCh(); goto case 21;} - else if (ch == 'n') {AddCh(); goto case 27;} - else if (ch == '+') {AddCh(); goto case 33;} - else if (ch == '-') {AddCh(); goto case 39;} - else {t.kind = 3; break;} + if (ch == 'o') {AddCh(); goto case 47;} + else {goto case 0;} case 47: - if (ch == '"') {AddCh(); goto case 49;} - else if (ch <= 9 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= '!' || ch >= '#' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 6;} - else if (ch == 92) {AddCh(); goto case 47;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 48;} else {goto case 0;} case 48: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 50;} - else if (ch == '-') {AddCh(); goto case 51;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 48;} + else if (ch == 'e') {AddCh(); goto case 49;} else {goto case 0;} case 49: - recEnd = pos; recKind = 4; - if (ch == '"') {AddCh(); goto case 7;} - else if (ch <= 9 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= '!' || ch >= '#' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 6;} - else if (ch == 92) {AddCh(); goto case 47;} - else {t.kind = 4; break;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 50;} + else {goto case 0;} case 50: - recEnd = pos; recKind = 5; + recEnd = pos; recKind = 7; if (ch >= '0' && ch <= '9') {AddCh(); goto case 50;} - else if (ch == 'f') {AddCh(); goto case 17;} - else {t.kind = 5; break;} + else {t.kind = 7; break;} case 51: - if (ch >= '0' && ch <= '9') {AddCh(); goto case 50;} - else {goto case 0;} + recEnd = pos; recKind = 3; + if (ch >= '0' && ch <= '9') {AddCh(); goto case 51;} + else if (ch == 'b') {AddCh(); goto case 3;} + else if (ch == 'e') {AddCh(); goto case 8;} + else if (ch == '.') {AddCh(); goto case 11;} + else {t.kind = 3; break;} case 52: - {t.kind = 9; break;} + recEnd = pos; recKind = 3; + if (ch >= '0' && ch <= '9') {AddCh(); goto case 51;} + else if (ch == 'b') {AddCh(); goto case 3;} + else if (ch == 'e') {AddCh(); goto case 8;} + else if (ch == '.') {AddCh(); goto case 11;} + else if (ch == 'x') {AddCh(); goto case 17;} + else if (ch == 'N') {AddCh(); goto case 27;} + else if (ch == 'n') {AddCh(); goto case 33;} + else if (ch == '+') {AddCh(); goto case 39;} + else if (ch == '-') {AddCh(); goto case 45;} + else {t.kind = 3; break;} case 53: - {t.kind = 10; break;} + if (ch == '"') {AddCh(); goto case 55;} + else if (ch <= 9 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= '!' || ch >= '#' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 6;} + else if (ch == 92) {AddCh(); goto case 53;} + else {goto case 0;} case 54: - {t.kind = 11; break;} + if (ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'd' || ch == 'f') {AddCh(); goto case 20;} + else if (ch >= '0' && ch <= '9') {AddCh(); goto case 56;} + else if (ch == 'e') {AddCh(); goto case 54;} + else if (ch == '-') {AddCh(); goto case 21;} + else {goto case 0;} case 55: - {t.kind = 13; break;} + recEnd = pos; recKind = 4; + if (ch == '"') {AddCh(); goto case 7;} + else if (ch <= 9 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= '!' || ch >= '#' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 6;} + else if (ch == 92) {AddCh(); goto case 53;} + else {t.kind = 4; break;} case 56: - {t.kind = 18; break;} + if (ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'd') {AddCh(); goto case 20;} + else if (ch >= '0' && ch <= '9') {AddCh(); goto case 56;} + else if (ch == 'e') {AddCh(); goto case 54;} + else if (ch == 'f') {AddCh(); goto case 57;} + else {goto case 0;} case 57: - {t.kind = 19; break;} + if (ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'd' || ch == 'f') {AddCh(); goto case 20;} + else if (ch >= '0' && ch <= '9') {AddCh(); goto case 58;} + else if (ch == 'e') {AddCh(); goto case 54;} + else {goto case 0;} case 58: - {t.kind = 28; break;} + if (ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'd' || ch == 'f') {AddCh(); goto case 20;} + else if (ch >= '0' && ch <= '9') {AddCh(); goto case 58;} + else if (ch == 'e') {AddCh(); goto case 59;} + else {goto case 0;} case 59: - {t.kind = 51; break;} + if (ch >= '0' && ch <= '9') {AddCh(); goto case 60;} + else if (ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'd' || ch == 'f') {AddCh(); goto case 20;} + else if (ch == 'e') {AddCh(); goto case 54;} + else if (ch == '-') {AddCh(); goto case 21;} + else {goto case 0;} case 60: - {t.kind = 56; break;} + recEnd = pos; recKind = 7; + if (ch >= '0' && ch <= '9') {AddCh(); goto case 60;} + else if (ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'd') {AddCh(); goto case 20;} + else if (ch == 'e') {AddCh(); goto case 54;} + else if (ch == 'f') {AddCh(); goto case 57;} + else {t.kind = 7; break;} case 61: - {t.kind = 57; break;} + {t.kind = 9; break;} case 62: - {t.kind = 58; break;} + {t.kind = 10; break;} case 63: - {t.kind = 59; break;} + {t.kind = 11; break;} case 64: - {t.kind = 61; break;} + {t.kind = 13; break;} case 65: - if (ch == '&') {AddCh(); goto case 66;} - else {goto case 0;} + {t.kind = 18; break;} case 66: - {t.kind = 62; break;} + {t.kind = 19; break;} case 67: - {t.kind = 63; break;} + {t.kind = 28; break;} case 68: - {t.kind = 64; break;} + {t.kind = 51; break;} case 69: - {t.kind = 65; break;} + {t.kind = 56; break;} case 70: - {t.kind = 68; break;} + {t.kind = 57; break;} case 71: - {t.kind = 69; break;} + {t.kind = 58; break;} case 72: - {t.kind = 70; break;} + {t.kind = 59; break;} case 73: - {t.kind = 71; break;} + {t.kind = 61; break;} case 74: - {t.kind = 72; break;} + if (ch == '&') {AddCh(); goto case 75;} + else {goto case 0;} case 75: - {t.kind = 73; break;} + {t.kind = 62; break;} case 76: - {t.kind = 74; break;} + {t.kind = 63; break;} case 77: - {t.kind = 79; break;} + {t.kind = 64; break;} case 78: - {t.kind = 80; break;} + {t.kind = 65; break;} case 79: - {t.kind = 82; break;} + {t.kind = 68; break;} case 80: - {t.kind = 86; break;} + {t.kind = 69; break;} case 81: - {t.kind = 87; break;} + {t.kind = 70; break;} case 82: - {t.kind = 90; break;} + {t.kind = 71; break;} case 83: - {t.kind = 92; break;} + {t.kind = 72; break;} case 84: - {t.kind = 94; break;} + {t.kind = 73; break;} case 85: - {t.kind = 95; break;} + {t.kind = 74; break;} case 86: - {t.kind = 96; break;} + {t.kind = 79; break;} case 87: + {t.kind = 80; break;} + case 88: + {t.kind = 82; break;} + case 89: + {t.kind = 96; break;} + case 90: + {t.kind = 97; break;} + case 91: + {t.kind = 100; break;} + case 92: + {t.kind = 102; break;} + case 93: + {t.kind = 104; break;} + case 94: + {t.kind = 105; break;} + case 95: + {t.kind = 106; break;} + case 96: recEnd = pos; recKind = 76; - if (ch >= '0' && ch <= '9') {AddCh(); goto case 13;} + if (ch == '0') {AddCh(); goto case 16;} else {t.kind = 76; break;} - case 88: + case 97: recEnd = pos; recKind = 12; - if (ch == '=') {AddCh(); goto case 59;} - else if (ch == ':') {AddCh(); goto case 85;} + if (ch == '=') {AddCh(); goto case 68;} + else if (ch == ':') {AddCh(); goto case 94;} else {t.kind = 12; break;} - case 89: + case 98: recEnd = pos; recKind = 20; - if (ch == '=') {AddCh(); goto case 97;} - else if (ch == ':') {AddCh(); goto case 72;} + if (ch == '=') {AddCh(); goto case 106;} + else if (ch == ':') {AddCh(); goto case 81;} else {t.kind = 20; break;} - case 90: + case 99: recEnd = pos; recKind = 21; - if (ch == '=') {AddCh(); goto case 70;} + if (ch == '=') {AddCh(); goto case 79;} else {t.kind = 21; break;} - case 91: + case 100: recEnd = pos; recKind = 29; - if (ch == '|') {AddCh(); goto case 81;} + if (ch == '|') {AddCh(); goto case 90;} else {t.kind = 29; break;} - case 92: + case 101: recEnd = pos; recKind = 32; - if (ch == '=') {AddCh(); goto case 98;} + if (ch == '=') {AddCh(); goto case 107;} else {t.kind = 32; break;} - case 93: + case 102: recEnd = pos; recKind = 45; - if (ch == '*') {AddCh(); goto case 78;} + if (ch == '*') {AddCh(); goto case 87;} else {t.kind = 45; break;} - case 94: + case 103: recEnd = pos; recKind = 55; - if (ch == '|') {AddCh(); goto case 68;} - else if (ch == '{') {AddCh(); goto case 80;} + if (ch == '|') {AddCh(); goto case 77;} + else if (ch == '{') {AddCh(); goto case 89;} else {t.kind = 55; break;} - case 95: + case 104: recEnd = pos; recKind = 81; - if (ch == '=') {AddCh(); goto case 71;} + if (ch == '=') {AddCh(); goto case 80;} else {t.kind = 81; break;} - case 96: + case 105: recEnd = pos; recKind = 75; - if (ch == '+') {AddCh(); goto case 76;} + if (ch == '+') {AddCh(); goto case 85;} else {t.kind = 75; break;} - case 97: + case 106: recEnd = pos; recKind = 67; - if (ch == '=') {AddCh(); goto case 99;} + if (ch == '=') {AddCh(); goto case 108;} else {t.kind = 67; break;} - case 98: + case 107: recEnd = pos; recKind = 66; - if (ch == '>') {AddCh(); goto case 62;} + if (ch == '>') {AddCh(); goto case 71;} else {t.kind = 66; break;} - case 99: + case 108: recEnd = pos; recKind = 60; - if (ch == '>') {AddCh(); goto case 60;} + if (ch == '>') {AddCh(); goto case 69;} else {t.kind = 60; break;} } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/StandardVisitor.cs boogie-2.4.1+dfsg/Source/Core/StandardVisitor.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/StandardVisitor.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/StandardVisitor.cs 2019-10-25 18:17:05.000000000 +0000 @@ -308,6 +308,14 @@ node = (LambdaExpr)this.VisitBinderExpr(node); return node; } + public virtual Expr VisitLetExpr(LetExpr node) { + Contract.Requires(node != null); + Contract.Ensures(Contract.Result() != null); + node.Body = this.VisitExpr(node.Body); + node.Dummies = this.VisitVariableSeq(node.Dummies); + node.Rhss = this.VisitExprSeq(node.Rhss); + return node; + } public virtual Formal VisitFormal(Formal node) { Contract.Requires(node != null); Contract.Ensures(Contract.Result() != null); @@ -866,8 +874,14 @@ return (ForallExpr)this.VisitQuantifierExpr(node); } public override Expr VisitLambdaExpr(LambdaExpr node) { - Contract.Ensures(Contract.Result() == node); - return this.VisitBinderExpr(node); + Contract.Ensures(Contract.Result() == node); + return this.VisitBinderExpr(node); + } + public override Expr VisitLetExpr(LetExpr node) { + this.VisitExpr(node.Body); + this.VisitVariableSeq(node.Dummies); + this.VisitExprSeq(node.Rhss); + return node; } public override Formal VisitFormal(Formal node) { diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Util.cs boogie-2.4.1+dfsg/Source/Core/Util.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/Util.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/Util.cs 2019-10-25 18:17:05.000000000 +0000 @@ -83,8 +83,10 @@ public bool UseForComputingChecksums; private const int indent_size = 2; - protected static string Indent(int level) { + private int most_recent_indent_level = 0; + protected string Indent(int level) { Contract.Ensures(Contract.Result() != null); + most_recent_indent_level = level; return new string(' ', (indent_size * level)); } @@ -120,6 +122,7 @@ "requires", "return", "returns", + "rmode", "true", "type", "unique", @@ -179,17 +182,20 @@ } // figure out which is the next writer to use List tw = wstk.Count > 0 ? wstk.Peek().Value : null; + string indent_string; if (tw == null) { writer = actual_writer; + indent_string = new string(' ', indent_size * most_recent_indent_level + indent_size); } else { writer = tw.Last(); + indent_string = new string(' ', indent_size); } // write the strings (we would like to know WHERE we are in the document here) if (len > 80 /* - wstk.Count * 2 */) { for (int i = 0; i < ss.Count; i++) { if (i != ss.Count - 1) { writer.WriteLine(ss[i]); - writer.Write(" "); + writer.Write(indent_string); } else { writer.Write(ss[i]); } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/VCExp.cs boogie-2.4.1+dfsg/Source/Core/VCExp.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Core/VCExp.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Core/VCExp.cs 2019-10-25 18:17:05.000000000 +0000 @@ -27,6 +27,7 @@ // Say (DBG_WAS_VALID) or (DBG_WAS_INVALID) after query public bool ForceLogStatus = false; public int TimeLimit = 0; + public int ResourceLimit = 0; public int MemoryLimit = 0; public int Verbosity = 0; public string ProverPath; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Doomed/Doomed.csproj boogie-2.4.1+dfsg/Source/Doomed/Doomed.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Doomed/Doomed.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Doomed/Doomed.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -9,9 +9,9 @@ Properties Doomed BoogieDoomed - v4.0 + v4.5 512 - Client + 12.0.0 2.0 0 @@ -67,6 +67,7 @@ Full DoNotBuild 0 + false pdbonly @@ -109,6 +110,7 @@ Full DoNotBuild 0 + false true @@ -124,6 +126,7 @@ AnyCPU prompt MinimumRecommendedRules.ruleset + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Doomed/Doomed-NetCore.csproj boogie-2.4.1+dfsg/Source/Doomed/Doomed-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Doomed/Doomed-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Doomed/Doomed-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,34 @@ + + + + Library + BoogieDoomed + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + + + + + + + + false + + + + + version.cs + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ExecutionEngine/ExecutionEngine.cs boogie-2.4.1+dfsg/Source/ExecutionEngine/ExecutionEngine.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ExecutionEngine/ExecutionEngine.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/ExecutionEngine/ExecutionEngine.cs 2019-10-25 18:17:05.000000000 +0000 @@ -106,10 +106,14 @@ { Console.Write("{0} finished with {1} credible, {2} doomed{3}", CommandLineOptions.Clo.DescriptiveToolName, stats.VerifiedCount, stats.ErrorCount, stats.ErrorCount == 1 ? "" : "s"); } - else + else if (CommandLineOptions.Clo.ShowVerifiedProcedureCount) { Console.Write("{0} finished with {1} verified, {2} error{3}", CommandLineOptions.Clo.DescriptiveToolName, stats.VerifiedCount, stats.ErrorCount, stats.ErrorCount == 1 ? "" : "s"); } + else + { + Console.Write("{0} finished with {1} error{2}", CommandLineOptions.Clo.DescriptiveToolName, stats.ErrorCount, stats.ErrorCount == 1 ? "" : "s"); + } if (stats.InconclusiveCount != 0) { Console.Write(", {0} inconclusive{1}", stats.InconclusiveCount, stats.InconclusiveCount == 1 ? "" : "s"); @@ -122,6 +126,9 @@ { Console.Write(", {0} out of memory", stats.OutOfMemoryCount); } + if (stats.OutOfResourceCount != 0) { + Console.Write(", {0} out of resource", stats.OutOfResourceCount); + } Console.WriteLine(); Console.Out.Flush(); } @@ -188,12 +195,14 @@ public int VerifiedCount; public int InconclusiveCount; public int TimeoutCount; + public int OutOfResourceCount; public int OutOfMemoryCount; public long[] CachingActionCounts; public int CachedErrorCount; public int CachedVerifiedCount; public int CachedInconclusiveCount; public int CachedTimeoutCount; + public int CachedOutOfResourceCount; public int CachedOutOfMemoryCount; } @@ -483,6 +492,8 @@ PrintBplFile(CommandLineOptions.Clo.PrintFile, program, false, true, CommandLineOptions.Clo.PrettyPrint); } + CivlAttributes.DesugarYieldAssert(program); + LinearTypeChecker linearTypeChecker; CivlTypeChecker civlTypeChecker; PipelineOutcome oc = ResolveAndTypecheck(program, fileNames[fileNames.Count - 1], out linearTypeChecker, out civlTypeChecker); @@ -502,8 +513,8 @@ if (CommandLineOptions.Clo.StratifiedInlining == 0) { - Concurrency.Transform(linearTypeChecker, civlTypeChecker); - (new LinearEraser()).VisitProgram(program); + CivlVCGeneration.Transform(linearTypeChecker, civlTypeChecker); + linearTypeChecker.EraseLinearAnnotations(); if (CommandLineOptions.Clo.CivlDesugaredFile != null) { int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured; @@ -701,6 +712,7 @@ linearTypeChecker = null; civlTypeChecker = null; + YieldTypeChecker yieldTypeChecker = null; // ---------- Resolve ------------------------------------------------------------ @@ -739,21 +751,29 @@ civlTypeChecker = new CivlTypeChecker(program); civlTypeChecker.TypeCheck(); - if (civlTypeChecker.errorCount != 0) + if (civlTypeChecker.checkingContext.ErrorCount != 0) + { + Console.WriteLine("{0} type checking errors detected in {1}", civlTypeChecker.checkingContext.ErrorCount, GetFileNameForConsole(bplFileName)); + return PipelineOutcome.TypeCheckingError; + } + + yieldTypeChecker = new YieldTypeChecker(civlTypeChecker); + yieldTypeChecker.TypeCheck(); + if (yieldTypeChecker.checkingContext.ErrorCount != 0) { - Console.WriteLine("{0} type checking errors detected in {1}", civlTypeChecker.errorCount, GetFileNameForConsole(bplFileName)); + Console.WriteLine("{0} type checking errors detected in {1}", yieldTypeChecker.checkingContext.ErrorCount, GetFileNameForConsole(bplFileName)); return PipelineOutcome.TypeCheckingError; } - linearTypeChecker = new LinearTypeChecker(program); + linearTypeChecker = new LinearTypeChecker(program, civlTypeChecker); linearTypeChecker.TypeCheck(); - if (linearTypeChecker.errorCount == 0) + if (linearTypeChecker.checkingContext.ErrorCount == 0) { linearTypeChecker.Transform(); } else { - Console.WriteLine("{0} type checking errors detected in {1}", linearTypeChecker.errorCount, GetFileNameForConsole(bplFileName)); + Console.WriteLine("{0} type checking errors detected in {1}", linearTypeChecker.checkingContext.ErrorCount, GetFileNameForConsole(bplFileName)); return PipelineOutcome.TypeCheckingError; } @@ -838,10 +858,12 @@ #region Do some pre-abstract-interpretation preprocessing on the program // Doing lambda expansion before abstract interpretation means that the abstract interpreter // never needs to see any lambda expressions. (On the other hand, if it were useful for it - // to see lambdas, then it would be better to more lambda expansion until after infererence.) + // to see lambdas, then it would be better to more lambda expansion until after inference.) if (CommandLineOptions.Clo.ExpandLambdas) { LambdaHelper.ExpandLambdas(program); - //PrintBplFile ("-", program, true); + if (CommandLineOptions.Clo.PrintFile != null && CommandLineOptions.Clo.PrintLambdaLifting) { + PrintBplFile(CommandLineOptions.Clo.PrintFile, program, false, true, CommandLineOptions.Clo.PrettyPrint); + } } #endregion @@ -1544,6 +1566,11 @@ } } break; + case VCGen.Outcome.OutOfResource: + if (implName != null && implTok != null) { + errorInfo = errorInformationFactory.CreateErrorInformation(implTok, "Verification out of resource (" + implName + ")", requestId); + } + break; case VCGen.Outcome.OutOfMemory: if (implName != null && implTok != null) { @@ -1593,6 +1620,9 @@ case VCGen.Outcome.TimedOut: traceOutput = "timed out"; break; + case VCGen.Outcome.OutOfResource: + traceOutput = "out of resource"; + break; case VCGen.Outcome.OutOfMemory: traceOutput = "out of memory"; break; @@ -1629,6 +1659,10 @@ Interlocked.Increment(ref stats.TimeoutCount); if (wasCached) { Interlocked.Increment(ref stats.CachedTimeoutCount); } break; + case VCGen.Outcome.OutOfResource: + Interlocked.Increment(ref stats.OutOfResourceCount); + if (wasCached) { Interlocked.Increment(ref stats.CachedOutOfResourceCount); } + break; case VCGen.Outcome.OutOfMemory: Interlocked.Increment(ref stats.OutOfMemoryCount); if (wasCached) { Interlocked.Increment(ref stats.CachedOutOfMemoryCount); } @@ -1725,6 +1759,9 @@ { cause = "Out of memory on"; } + else if (outcome == VCGen.Outcome.OutOfResource) { + cause = "Out of resource on"; + } var callError = error as CallCounterexample; var returnError = error as ReturnCounterexample; @@ -1760,12 +1797,18 @@ errorInfo = errorInformationFactory.CreateErrorInformation(assertError.FailingAssert.tok, "This loop invariant might not hold on entry.", assertError.RequestId, assertError.OriginalRequestId, cause); errorInfo.BoogieErrorCode = "BP5004"; errorInfo.Kind = ErrorKind.InvariantEntry; + if ((assertError.FailingAssert.ErrorData as string) != null) { + errorInfo.AddAuxInfo(assertError.FailingAssert.tok, assertError.FailingAssert.ErrorData as string, "Related message"); + } } else if (assertError.FailingAssert is LoopInvMaintainedAssertCmd) { errorInfo = errorInformationFactory.CreateErrorInformation(assertError.FailingAssert.tok, "This loop invariant might not be maintained by the loop.", assertError.RequestId, assertError.OriginalRequestId, cause); errorInfo.BoogieErrorCode = "BP5005"; errorInfo.Kind = ErrorKind.InvariantMaintainance; + if ((assertError.FailingAssert.ErrorData as string) != null) { + errorInfo.AddAuxInfo(assertError.FailingAssert.tok, assertError.FailingAssert.ErrorData as string, "Related message"); + } } else { diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ExecutionEngine/ExecutionEngine.csproj boogie-2.4.1+dfsg/Source/ExecutionEngine/ExecutionEngine.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ExecutionEngine/ExecutionEngine.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/ExecutionEngine/ExecutionEngine.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -9,7 +9,7 @@ Properties ExecutionEngine BoogieExecutionEngine - v4.0 + v4.5 512 @@ -70,6 +70,7 @@ Full DoNotBuild 0 + false pdbonly @@ -112,6 +113,7 @@ Full DoNotBuild 0 + false true @@ -121,6 +123,7 @@ AnyCPU prompt MinimumRecommendedRules.ruleset + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ExecutionEngine/ExecutionEngine-NetCore.csproj boogie-2.4.1+dfsg/Source/ExecutionEngine/ExecutionEngine-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ExecutionEngine/ExecutionEngine-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/ExecutionEngine/ExecutionEngine-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,43 @@ + + + + Library + BoogieExecutionEngine + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + version.cs + + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ExecutionEngine/VerificationResultCache.cs boogie-2.4.1+dfsg/Source/ExecutionEngine/VerificationResultCache.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ExecutionEngine/VerificationResultCache.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/ExecutionEngine/VerificationResultCache.cs 2019-10-25 18:17:05.000000000 +0000 @@ -55,7 +55,6 @@ sealed class CachedVerificationResultInjector : StandardVisitor { - readonly IEnumerable Implementations; readonly Program Program; // TODO(wuestholz): We should probably increase the threshold to something like 2 seconds. static readonly double TimeThreshold = -1.0d; @@ -82,9 +81,8 @@ } } - CachedVerificationResultInjector(Program program, IEnumerable implementations) + CachedVerificationResultInjector(Program program) { - Implementations = implementations; Program = program; } @@ -162,7 +160,7 @@ public static void Inject(Program program, IEnumerable implementations, string requestId, string programId, out long[] cachingActionCounts) { - var eai = new CachedVerificationResultInjector(program, implementations); + var eai = new CachedVerificationResultInjector(program); cachingActionCounts = new long[Enum.GetNames(typeof(VC.ConditionGeneration.CachingAction)).Length]; var run = new CachedVerificationResultInjectorRun { Start = DateTime.UtcNow, ImplementationCount = implementations.Count(), CachingActionCounts = cachingActionCounts }; @@ -446,12 +444,11 @@ public override Expr VisitNAryExpr(NAryExpr node) { - if (currentTrigger != null) - { - // We found a function call within a trigger of a quantifier expression. - var funCall = node.Fun as FunctionCall; - if (funCall != null && funCall.Func != null && funCall.Func.Checksum != null && funCall.Func.Checksum != "stable") - { + var funCall = node.Fun as FunctionCall; + if (funCall != null && funCall.Func != null && funCall.Func.Checksum != null && funCall.Func.Checksum != "stable") { + if (funCall.ArgumentCount == 0 || currentTrigger != null) { + // We found a function call within a trigger of a quantifier expression, or the function does not take any + // arguments so we don't expect it ever to sit inside a quantifier. funCall.Func.AddOtherDefinitionAxiom(currentAxiom); } } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Graph/Graph.csproj boogie-2.4.1+dfsg/Source/Graph/Graph.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Graph/Graph.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Graph/Graph.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,7 +10,7 @@ Properties Graph BoogieGraph - v4.0 + v4.5 512 1 true @@ -34,7 +34,7 @@ false false true - Client + true @@ -74,6 +74,7 @@ Full %28none%29 AllRules.ruleset + false pdbonly @@ -83,6 +84,7 @@ prompt 4 AllRules.ruleset + false true @@ -99,6 +101,7 @@ true 4 false + false true @@ -141,6 +144,7 @@ 0 4 false + false true @@ -150,6 +154,7 @@ AnyCPU prompt AllRules.ruleset + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Graph/Graph-NetCore.csproj boogie-2.4.1+dfsg/Source/Graph/Graph-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Graph/Graph-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Graph/Graph-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,27 @@ + + + + Library + BoogieGraph + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + false + + + + + version.cs + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Houdini/AbstractHoudini.cs boogie-2.4.1+dfsg/Source/Houdini/AbstractHoudini.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Houdini/AbstractHoudini.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Houdini/AbstractHoudini.cs 2019-10-25 18:17:05.000000000 +0000 @@ -217,7 +217,7 @@ if (CommandLineOptions.Clo.Trace) Console.WriteLine("Time taken = " + inc.TotalSeconds.ToString()); - if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory) + if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory || proverOutcome == ProverInterface.Outcome.OutOfResource) { // pick some function; make it true and keep going bool changed = false; @@ -2794,7 +2794,7 @@ if (UseBilateralAlgo) { - if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory) + if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory || proverOutcome == ProverInterface.Outcome.OutOfResource) { if(CommandLineOptions.Clo.Trace) Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name); @@ -2819,7 +2819,7 @@ } else { - if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory) + if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory || proverOutcome == ProverInterface.Outcome.OutOfResource) { if (CommandLineOptions.Clo.Trace) Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Houdini/Checker.cs boogie-2.4.1+dfsg/Source/Houdini/Checker.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Houdini/Checker.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Houdini/Checker.cs 2019-10-25 18:17:05.000000000 +0000 @@ -352,7 +352,7 @@ outcome = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions, out unsatisfiedSoftAssumptions, handler); hardAssumptions.RemoveAt(hardAssumptions.Count - 1); - if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.Undetermined) + if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.OutOfResource || outcome == ProverInterface.Outcome.Undetermined) break; var reason = new HashSet(); @@ -382,7 +382,7 @@ var unsatisfiedSoftAssumptions2 = new List(); outcome = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions2, out unsatisfiedSoftAssumptions2, handler); - if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.Undetermined) + if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.OutOfResource|| outcome == ProverInterface.Outcome.Undetermined) break; unsatisfiedSoftAssumptions2.Iter(i => reason.Remove(softAssumptions2[i].ToString())); @@ -406,7 +406,7 @@ } } while (false); - if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.Undetermined) + if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.OutOfResource || outcome == ProverInterface.Outcome.Undetermined) { Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=red ];", refutedConstant.Name, "TimeOut", descriptiveName); } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Houdini/Houdini.csproj boogie-2.4.1+dfsg/Source/Houdini/Houdini.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Houdini/Houdini.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Houdini/Houdini.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,9 +10,10 @@ Properties Microsoft.Boogie.Houdini BoogieHoudini - v4.0 + v4.5 512 0 + true @@ -22,6 +23,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -30,6 +32,7 @@ TRACE prompt 4 + false true @@ -72,6 +75,7 @@ 0 4 false + false true @@ -81,6 +85,7 @@ AnyCPU prompt MinimumRecommendedRules.ruleset + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Houdini/Houdini-NetCore.csproj boogie-2.4.1+dfsg/Source/Houdini/Houdini-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Houdini/Houdini-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Houdini/Houdini-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,36 @@ + + + + Library + BoogieHoudini + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + + + + + + + + + + false + + + + + version.cs + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Model/Model.cs boogie-2.4.1+dfsg/Source/Model/Model.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Model/Model.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Model/Model.cs 2019-10-25 18:17:05.000000000 +0000 @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // @@ -45,6 +45,7 @@ public enum ElementKind { Integer, + Real, BitVector, Boolean, Uninterpreted, @@ -108,6 +109,13 @@ public override string ToString() { return Numeral.ToString(); } } + public class Real : Number + { + internal Real(Model p, string n) : base(p, n) { } + public override ElementKind Kind { get { return ElementKind.Real; } } + public override string ToString() { return Numeral.ToString(); } + } + public class BitVector : Number { internal BitVector(Model p, string n, int sz) : base(p, n) { Size = sz; } @@ -422,7 +430,7 @@ if (name.StartsWith("bv") && name.Length > 4 && Char.IsDigit(name[2])) name = name.Substring(2); - if (Char.IsDigit(name[0]) || name[0] == '-') { + if (Char.IsDigit(name[0]) || (name[0] == '-' && name.Length > 1 && Char.IsDigit(name[1]))) { int col = name.IndexOf("bv"); int szi = -1; @@ -444,14 +452,18 @@ } } - for (int i = 1; i < name.Length; ++i) - if (!Char.IsDigit(name[i])) - return null; - - if (szi > 0) - return new BitVector(this, name, szi); - else - return new Integer(this, name); + var allDigits = new Regex(@"^-?[0-9]*$"); + var real = new Regex(@"^-?[0-9]+\.[0-9]+$"); + if (allDigits.IsMatch(name)) { + if (szi > 0) + return new BitVector(this, name, szi); + else + return new Integer(this, name); + } else if (real.IsMatch(name)) { + return new Real(this, name); + } else { + return null; + } } else if (name[0] == '*' || name.StartsWith("val!") || name.Contains("!val!")) { return new Uninterpreted(this, name); } else if (name.StartsWith("as-array[") && name.EndsWith("]")) { @@ -618,6 +630,7 @@ public IEnumerable States { get { return states; } } public readonly Element True, False; public readonly CapturedState InitialState; + public bool ModelHasStatesAlready = false; public bool HasFunc(string name) { @@ -663,29 +676,40 @@ public void Write(System.IO.TextWriter wr) { wr.WriteLine("*** MODEL"); - foreach (var f in Functions.OrderBy(f => f.Name)) + foreach (var f in Functions.OrderBy(f => f.Name)) { if (f.Arity == 0) { wr.WriteLine("{0} -> {1}", f.Name, f.GetConstant()); } - foreach (var f in Functions) + } + foreach (var f in Functions.OrderBy(f => f.Name)) { if (f.Arity != 0) { - wr.WriteLine("{0} -> {1}", f.Name, "{"); + wr.WriteLine("{0} -> {{", f.Name); + // first, add the entries to a list, so that they can be sorted before printing + List> entries = new List>(); foreach (var app in f.Apps) { - wr.Write(" "); - foreach (var a in app.Args) - wr.Write("{0} ", a); - wr.WriteLine("-> {0}", app.Result); + var args = ""; + foreach (var a in app.Args) { + args += a + " "; + } + entries.Add(new Tuple(args, app.Result)); } - if (f.Else != null) + foreach (var entry in entries.OrderBy(pair => pair.Item1)) { + wr.WriteLine(" {0}-> {1}", entry.Item1, entry.Item2); + } + if (f.Else != null) { wr.WriteLine(" else -> {0}", f.Else); + } wr.WriteLine("}"); } + } foreach (var s in States) { - if (s == InitialState && s.VariableCount == 0) + if (s == InitialState && s.VariableCount == 0) { continue; + } wr.WriteLine("*** STATE {0}", s.Name); - foreach (var v in s.Variables) + foreach (var v in s.Variables.OrderBy(nm => nm)) { wr.WriteLine(" {0} -> {1}", v, s.TryGet(v)); + } wr.WriteLine("*** END_STATE", s.Name); } wr.WriteLine("*** END_MODEL"); @@ -704,3 +728,4 @@ } } } + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Model/Model.csproj boogie-2.4.1+dfsg/Source/Model/Model.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Model/Model.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Model/Model.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,9 +10,9 @@ Properties Microsoft.Boogie BoogieModel - v4.0 + v4.5 512 - Client + 0 @@ -23,6 +23,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -31,6 +32,7 @@ TRACE prompt 4 + false true @@ -79,6 +81,7 @@ 0 4 false + false true @@ -88,6 +91,7 @@ AnyCPU prompt MinimumRecommendedRules.ruleset + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Model/Model-NetCore.csproj boogie-2.4.1+dfsg/Source/Model/Model-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Model/Model-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Model/Model-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,28 @@ + + + + Library + BoogieModel + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + false + + + + + version.cs + + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ModelViewer/ModelViewer.csproj boogie-2.4.1+dfsg/Source/ModelViewer/ModelViewer.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ModelViewer/ModelViewer.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/ModelViewer/ModelViewer.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,8 +10,8 @@ Properties Microsoft.Boogie.ModelViewer BoogieModelViewer - v4.0 - Client + v4.5 + 512 publish\ true @@ -39,6 +39,7 @@ DEBUG;TRACE prompt 4 + false AnyCPU @@ -48,6 +49,7 @@ TRACE prompt 4 + false true @@ -99,6 +101,7 @@ 0 4 false + false @@ -111,13 +114,13 @@ AnyCPU prompt MinimumRecommendedRules.ruleset + false - diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ModelViewer/Properties/Resources.Designer.cs boogie-2.4.1+dfsg/Source/ModelViewer/Properties/Resources.Designer.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ModelViewer/Properties/Resources.Designer.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/ModelViewer/Properties/Resources.Designer.cs 2019-10-25 18:17:05.000000000 +0000 @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.1 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ModelViewer/Properties/Settings.Designer.cs boogie-2.4.1+dfsg/Source/ModelViewer/Properties/Settings.Designer.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ModelViewer/Properties/Settings.Designer.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/ModelViewer/Properties/Settings.Designer.cs 2019-10-25 18:17:05.000000000 +0000 @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.1 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -12,7 +12,7 @@ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ParserHelper/ParserHelper.csproj boogie-2.4.1+dfsg/Source/ParserHelper/ParserHelper.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ParserHelper/ParserHelper.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/ParserHelper/ParserHelper.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,9 +10,9 @@ Properties ParserHelper BoogieParserHelper - v4.0 + v4.5 512 - Client + 1 @@ -45,6 +45,7 @@ ..\..\baseline.xml Full %28none%29 + false pdbonly @@ -53,6 +54,7 @@ TRACE prompt 4 + false true @@ -101,6 +103,7 @@ 0 4 false + false true @@ -110,6 +113,7 @@ AnyCPU prompt MinimumRecommendedRules.ruleset + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ParserHelper/ParserHelper-NetCore.csproj boogie-2.4.1+dfsg/Source/ParserHelper/ParserHelper-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/ParserHelper/ParserHelper-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/ParserHelper/ParserHelper-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,27 @@ + + + + Library + BoogieParserHelper + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + false + + + + + version.cs + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Predication/Predication.csproj boogie-2.4.1+dfsg/Source/Predication/Predication.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Predication/Predication.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Predication/Predication.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -9,9 +9,9 @@ Properties Predication BoogiePredication - v4.0 + v4.5 512 - Client + 12.0.0 2.0 0 @@ -67,6 +67,7 @@ Full DoNotBuild 0 + false pdbonly @@ -109,6 +110,7 @@ Full DoNotBuild 0 + false true @@ -124,6 +126,7 @@ AnyCPU prompt MinimumRecommendedRules.ruleset + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Predication/Predication-NetCore.csproj boogie-2.4.1+dfsg/Source/Predication/Predication-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Predication/Predication-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Predication/Predication-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,32 @@ + + + + Library + BoogiePredication + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + + + + + + false + + + + + version.cs + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Predication/SmartBlockPredicator.cs boogie-2.4.1+dfsg/Source/Predication/SmartBlockPredicator.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Predication/SmartBlockPredicator.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Predication/SmartBlockPredicator.cs 2019-10-25 18:17:05.000000000 +0000 @@ -75,6 +75,9 @@ if (cmd is CallCmd) { var cCmd = (CallCmd)cmd; Debug.Assert(useProcedurePredicates(cCmd.Proc)); + foreach (IdentifierExpr e in cCmd.Outs) { + cCmd.Ins.Add(e); + } cCmd.Ins.Insert(0, p != null ? p : Expr.True); cmdSeq.Add(cCmd); } else if (p == null) { @@ -554,16 +557,40 @@ bool upp = useProcedurePredicates(proc); if (upp) { var dwf = (DeclWithFormals)decl; + // Copy InParams, as the list is shared between impl and proc + var inParams = new List(dwf.InParams); + var fpVar = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "_P", Microsoft.Boogie.Type.Bool), /*incoming=*/true); - dwf.InParams = new List( - (new Variable[] {fpVar}.Concat(dwf.InParams.Cast())) - .ToArray()); + inParams.Insert(0, fpVar); + var fpIdentifierExpr = new IdentifierExpr(Token.NoToken, fpVar); + + // Add in-parameters for all out-parameters. These new in-parameters + // are used to ensure we preserve the value of the variable assigned + // to when the passed predicate value is false. + var newEqParamExprs = new List(); + var newAssignCmds = new List(); + foreach (Variable outV in dwf.OutParams) { + var inV = new Formal(Token.NoToken, + new TypedIdent(Token.NoToken, "_V" + outV.TypedIdent.Name, + outV.TypedIdent.Type), + /*incoming=*/true); + inParams.Add(inV); + + var inVExpr = new IdentifierExpr(Token.NoToken, inV); + var outVExpr = new IdentifierExpr(Token.NoToken, outV); + newEqParamExprs.Add(Expr.Imp(Expr.Not(fpIdentifierExpr), Expr.Eq(inVExpr, outVExpr))); + newAssignCmds.Add(new AssignCmd(Token.NoToken, + new List { new SimpleAssignLhs(Token.NoToken, outVExpr) }, + new List { new NAryExpr(Token.NoToken, + new IfThenElse(Token.NoToken), + new List { fpIdentifierExpr, outVExpr, inVExpr })})); + } + dwf.InParams = inParams; if (impl == null) { - var fpIdentifierExpr = new IdentifierExpr(Token.NoToken, fpVar); foreach (Requires r in proc.Requires) { new EnabledReplacementVisitor(fpIdentifierExpr, Expr.True).VisitExpr(r.Condition); if (!QKeyValue.FindBoolAttribute(r.Attributes, "do_not_predicate")) { @@ -576,6 +603,16 @@ e.Condition = Expr.Imp(fpIdentifierExpr, e.Condition); } } + foreach (Expr e in newEqParamExprs) { + proc.Ensures.Add(new Ensures(false, e)); + } + } else { + try { + new SmartBlockPredicator(p, impl, useProcedurePredicates, uni).PredicateImplementation(); + foreach (AssignCmd c in newAssignCmds) { + impl.Blocks.First().Cmds.Insert(0, c); + } + } catch (Program.IrreducibleLoopException) { } } } else { if (impl == null) { @@ -585,14 +622,12 @@ foreach (Ensures e in proc.Ensures) { new EnabledReplacementVisitor(Expr.True, Expr.True).VisitExpr(e.Condition); } + } else { + try { + new SmartBlockPredicator(p, impl, useProcedurePredicates, uni).PredicateImplementation(); + } catch (Program.IrreducibleLoopException) { } } } - - if (impl != null) { - try { - new SmartBlockPredicator(p, impl, useProcedurePredicates, uni).PredicateImplementation(); - } catch (Program.IrreducibleLoopException) { } - } } } } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Predication/UniformityAnalyser.cs boogie-2.4.1+dfsg/Source/Predication/UniformityAnalyser.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Predication/UniformityAnalyser.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Predication/UniformityAnalyser.cs 2019-10-25 18:17:05.000000000 +0000 @@ -204,6 +204,10 @@ { newIns.Add(s); } + foreach (string s in outParameters[Proc.Name]) + { + newIns.Add("_V" + s); + } inParameters[Proc.Name] = newIns; } } @@ -236,7 +240,7 @@ foreach (Block b in Impl.Blocks) { Analyse(Impl, b.Cmds, false); } - + return; } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/CVC4.cs boogie-2.4.1+dfsg/Source/Provers/SMTLib/CVC4.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/CVC4.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/CVC4.cs 2019-10-25 18:17:05.000000000 +0000 @@ -19,6 +19,7 @@ } public static string ExecutablePath() + // throws ProverException, System.IO.FileNotFoundException; { if (_proverPath == null) FindExecutable(); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/ProverInterface.cs boogie-2.4.1+dfsg/Source/Provers/SMTLib/ProverInterface.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/ProverInterface.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/ProverInterface.cs 2019-10-25 18:17:05.000000000 +0000 @@ -18,6 +18,7 @@ using Microsoft.Boogie.Clustering; using Microsoft.Boogie.TypeErasure; using System.Text; +using System.Numerics; using RPFP = Microsoft.Boogie.RPFP; @@ -27,7 +28,7 @@ { private readonly SMTLibProverContext ctx; private VCExpressionGenerator gen; - private readonly SMTLibProverOptions options; + protected readonly SMTLibProverOptions options; private bool usingUnsatCore; private RPFP rpfp = null; @@ -168,12 +169,15 @@ } internal TypeAxiomBuilder AxBuilder { get; private set; } - internal readonly UniqueNamer Namer; + private TypeAxiomBuilder CachedAxBuilder; + private UniqueNamer CachedNamer; + internal UniqueNamer Namer { get; private set; } readonly TypeDeclCollector DeclCollector; protected SMTLibProcess Process; readonly List proverErrors = new List(); readonly List proverWarnings = new List(); - readonly StringBuilder common = new StringBuilder(); + StringBuilder common = new StringBuilder(); + private string CachedCommon = null; protected TextWriter currentLogFile; protected volatile ErrorHandler currentErrorHandler; @@ -261,7 +265,7 @@ // Set produce-unsat-cores last. It seems there's a bug in Z3 where if we set it earlier its value // gets reset by other set-option commands ( https://z3.codeplex.com/workitem/188 ) - if (CommandLineOptions.Clo.PrintNecessaryAssumes || (CommandLineOptions.Clo.ContractInfer && (CommandLineOptions.Clo.UseUnsatCoreForContractInfer || CommandLineOptions.Clo.ExplainHoudini))) + if (CommandLineOptions.Clo.PrintNecessaryAssumes || CommandLineOptions.Clo.EnableUnSatCoreExtract == 1 ||(CommandLineOptions.Clo.ContractInfer && (CommandLineOptions.Clo.UseUnsatCoreForContractInfer || CommandLineOptions.Clo.ExplainHoudini))) { SendCommon("(set-option :produce-unsat-cores true)"); this.usingUnsatCore = true; @@ -304,36 +308,31 @@ sccs.Compute(); foreach (GraphUtil.SCC scc in sccs) { - string datatypeString = ""; + string datatypesString = ""; + string datatypeConstructorsString = ""; foreach (CtorType datatype in scc) { - datatypeString += "(" + SMTLibExprLineariser.TypeToString(datatype) + " "; + datatypesString += "(" + SMTLibExprLineariser.TypeToString(datatype) + " 0)"; + string datatypeConstructorString = ""; foreach (Function f in ctx.KnownDatatypeConstructors[datatype]) { string quotedConstructorName = Namer.GetQuotedName(f, f.Name); - if (f.InParams.Count == 0) + datatypeConstructorString += "(" + quotedConstructorName + " "; + foreach (Variable v in f.InParams) { - datatypeString += quotedConstructorName + " "; - } - else - { - datatypeString += "(" + quotedConstructorName + " "; - foreach (Variable v in f.InParams) - { - string quotedSelectorName = Namer.GetQuotedName(v, v.Name + "#" + f.Name); - datatypeString += "(" + quotedSelectorName + " " + DeclCollector.TypeToStringReg(v.TypedIdent.Type) + ") "; - } - datatypeString += ") "; + string quotedSelectorName = Namer.GetQuotedName(v, v.Name + "#" + f.Name); + datatypeConstructorString += "(" + quotedSelectorName + " " + DeclCollector.TypeToStringReg(v.TypedIdent.Type) + ") "; } + datatypeConstructorString += ") "; } - datatypeString += ") "; + datatypeConstructorsString += "(" + datatypeConstructorString + ") "; } List decls = DeclCollector.GetNewDeclarations(); foreach (string decl in decls) { SendCommon(decl); } - SendCommon("(declare-datatypes () (" + datatypeString + "))"); + SendCommon("(declare-datatypes (" + datatypesString + ") (" + datatypeConstructorsString + "))"); } } if (CommandLineOptions.Clo.ProverPreamble != null) @@ -354,6 +353,8 @@ else AddAxiom(VCExpr2String(axioms, -1)); AxiomsAreSetup = true; + CachedAxBuilder = AxBuilder; + CachedNamer = Namer; } } @@ -363,6 +364,14 @@ return 0; } + private void FlushAndCacheCommons() + { + FlushAxioms(); + if (CachedCommon == null) { + CachedCommon = common.ToString(); + } + } + private void FlushAxioms() { TypeDecls.Iter(SendCommon); @@ -415,6 +424,15 @@ } PrepareCommon(); + FlushAndCacheCommons(); + + if (HasReset) { + AxBuilder = (TypeAxiomBuilder)CachedAxBuilder.Clone(); + Namer = (SMTLibNamer)CachedNamer.Clone(); + Namer.ResetLabelCount(); + DeclCollector.SetNamer(Namer); + DeclCollector.Push(); + } OptimizationRequests.Clear(); @@ -439,6 +457,11 @@ Process.Inspector.NewProblem(descriptiveName, vc, handler); } + if (HasReset) { + DeclCollector.Pop(); + common = new StringBuilder(CachedCommon); + HasReset = false; + } SendCheckSat(); FlushLogFile(); } @@ -470,6 +493,7 @@ currentLogFile.WriteLine(c); } } + HasReset = true; } } @@ -1221,6 +1245,15 @@ } } + private void ReportProverError(string err) + { + var handler = currentErrorHandler; + if (handler != null) + { + handler.OnProverError(err); + } + } + protected void HandleProverError(string s) { s = s.Replace("\r", ""); @@ -1247,6 +1280,7 @@ proverErrors.Add(s); Console.WriteLine("Prover error: " + s); } + ReportProverError(s); } [NoDefaultContract] @@ -1444,7 +1478,7 @@ if (globalResult == Outcome.Undetermined) globalResult = result; - if (result == Outcome.Invalid || result == Outcome.TimeOut || result == Outcome.OutOfMemory) { + if (result == Outcome.Invalid || result == Outcome.TimeOut || result == Outcome.OutOfMemory || result == Outcome.OutOfResource) { IList xlabels; if (CommandLineOptions.Clo.UseLabels) { labels = GetLabelsInfo(); @@ -1464,7 +1498,7 @@ labels = CalculatePath(handler.StartingProcId()); xlabels = labels; } - Model model = (result == Outcome.TimeOut || result == Outcome.OutOfMemory) ? null : + Model model = (result == Outcome.TimeOut || result == Outcome.OutOfMemory || result == Outcome.OutOfResource) ? null : GetErrorModel(); handler.OnModel(xlabels, model, result); } @@ -1480,7 +1514,7 @@ if (CommandLineOptions.Clo.UseLabels) { var negLabels = labels.Where(l => l.StartsWith("@")).ToArray(); var posLabels = labels.Where(l => !l.StartsWith("@")); - Func lbl = (s) => SMTLibNamer.QuoteId(SMTLibNamer.LabelVar(s)); + Func lbl = (s) => SMTLibNamer.QuoteId(Namer.LabelVar(s)); if (!options.MultiTraces) posLabels = Enumerable.Empty(); var conjuncts = posLabels.Select(s => "(not " + lbl(s) + ")").Concat(negLabels.Select(lbl)).ToArray(); @@ -1522,7 +1556,11 @@ } SendThisVC("(push 1)"); - SendThisVC(string.Format("(set-option :{0} {1})", Z3.SetTimeoutOption(), (0 < tla && tla < timeLimit) ? tla : timeLimit)); + // FIXME: Gross. Timeout should be set in one place! This is also Z3 specific! + int newTimeout = (0 < tla && tla < timeLimit) ? tla : timeLimit; + if (newTimeout > 0) { + SendThisVC(string.Format("(set-option :{0} {1})", Z3.SetTimeoutOption(), newTimeout)); + } popLater = true; SendThisVC(string.Format("; checking split VC with {0} unverified assertions", split.Count)); @@ -1572,22 +1610,17 @@ private class SMTErrorModelConverter { - private struct SMTDataType { - public string Constructor; - public List Types; - } - private List ErrorModelTodo; private SMTLibProcessTheoremProver Parent; private StringBuilder ErrorModel = new StringBuilder(); private HashSet TopLevelProcessed = new HashSet(); private int NumNewArrays = 0; private Dictionary SortSet = new Dictionary(); - private Dictionary DataTypes = new Dictionary(); + private Dictionary>> DataTypes = new Dictionary>>(); private Dictionary Functions = new Dictionary(); public SMTErrorModelConverter(SExpr _ErrorModel, SMTLibProcessTheoremProver _Parent) { - ErrorModelTodo = _ErrorModel.Arguments.ToList();; + ErrorModelTodo = _ErrorModel.Arguments.ToList(); Parent = _Parent; } @@ -1684,7 +1717,7 @@ } if (SortSet.ContainsKey(type.Name) && SortSet[type.Name] == 0) { - var prefix = "@uc_T_" + type.Name.Substring(2) + "_"; + var prefix = "@uc_T@" + type.Name.Substring(2) + "_"; if (element.Name.StartsWith(prefix)) { m.Append(type.Name + "!val!" + element.Name.Substring(prefix.Length)); return; @@ -1698,12 +1731,12 @@ } if (DataTypes.ContainsKey(type.Name) && - DataTypes[type.Name].Constructor == element.Name && - element.ArgCount == DataTypes[type.Name].Types.Count) { + DataTypes[type.Name].ContainsKey(element.Name) && + element.ArgCount == DataTypes[type.Name][element.Name].Count) { m.Append("(" + element.Name); for (int i = 0; i < element.ArgCount; ++i) { m.Append(" "); - ConstructComplexValue(element[i], DataTypes[type.Name].Types[i], m); + ConstructComplexValue(element[i], DataTypes[type.Name][element.Name][i], m); } m.Append(")"); return; @@ -1718,10 +1751,13 @@ ConstructFunctionArguments(arguments[0], argTypes, argValues); ConstructFunctionArguments(arguments[1], argTypes, argValues); } else if (arguments.Name == "=" && - (arguments[0].Name.StartsWith("_ufmt_") || arguments[0].Name.StartsWith("x!"))) { + (arguments[0].Name.StartsWith("_ufmt_") || arguments[0].Name.StartsWith("x!") || + arguments[0].Name.StartsWith("_arg_"))) { int argNum; if (arguments[0].Name.StartsWith("_ufmt_")) argNum = System.Convert.ToInt32(arguments[0].Name.Substring("_uftm_".Length)) - 1; + else if (arguments[0].Name.StartsWith("_arg_")) + argNum = System.Convert.ToInt32(arguments[0].Name.Substring("_arg_".Length)) - 1; else /* if (arguments[0].Name.StartsWith("x!")) */ argNum = System.Convert.ToInt32(arguments[0].Name.Substring("x!".Length)) - 1; if (argNum < 0 || argNum >= argTypes.Count) { @@ -1762,7 +1798,7 @@ for (int i = 0; i < inType.ArgCount; ++i) { if (inType[i].Name != "_ufmt_" + (i + 1) && inType[i].Name != "x!" + (i + 1) && - !inType[i].Name.StartsWith("BOUND_VARIABLE_")) { + !inType[i].Name.StartsWith("BOUND_VARIABLE_") && inType[i].Name != "_arg_" + (i + 1)) { Parent.HandleProverError("Unexpected function argument: " + inType[i].Name); throw new BadExprFromProver (); } @@ -1802,27 +1838,20 @@ Parent.HandleProverError("Unexpected datatype: " + datatypes); throw new BadExprFromProver (); } - - SMTDataType dt = new SMTDataType(); SExpr typeDef = datatypes[1][0]; - - if (typeDef.ArgCount != 1) { - Parent.HandleProverError("Unexpected datatype: " + datatypes); - throw new BadExprFromProver (); - } - - dt.Constructor = typeDef[0].Name; - dt.Types = new List(); - - for (int i = 0; i < typeDef[0].ArgCount; ++i) { - if (typeDef[0][i].ArgCount != 1) { - Parent.HandleProverError("Unexpected datatype constructor: " + typeDef[0]); - throw new BadExprFromProver (); + Dictionary> dataTypeConstructors = new Dictionary>(); + for (int j = 0; j < typeDef.ArgCount; ++j) { + var argumentTypes = new List(); + for (int i = 0; i < typeDef[j].ArgCount; ++i) { + if (typeDef[j][i].ArgCount != 1) { + Parent.HandleProverError("Unexpected datatype constructor: " + typeDef[j]); + throw new BadExprFromProver(); + } + argumentTypes.Add(typeDef[j][i][0]); } - dt.Types.Add(typeDef[0][i][0]); + dataTypeConstructors[typeDef[j].Name] = argumentTypes; } - - DataTypes[typeDef.Name] = dt; + DataTypes[datatypes[0][0].Name] = dataTypeConstructors; } private void ConvertErrorModel(StringBuilder m) { @@ -1941,8 +1970,8 @@ break; if (res != null) HandleProverError("Expecting only one sequence of labels but got many"); - if (resp.Name == "labels" && resp.ArgCount >= 1) { - res = resp.Arguments.Select(a => a.Name.Replace("|", "")).ToArray(); + if (resp.Name == "labels") { + res = resp.Arguments.Select(a => Namer.AbsyLabel(a.Name.Replace("|", ""))).ToArray(); } else { HandleProverError("Unexpected prover response getting labels: " + resp.ToString()); @@ -1977,6 +2006,16 @@ case "objectives": // We ignore this. break; + case "error": + if (resp.Arguments.Length == 1 && resp.Arguments[0].IsId && + ( resp.Arguments[0].Name.Contains("max. resource limit exceeded") + || resp.Arguments[0].Name.Contains("resource limits reached"))) { + currentErrorHandler.OnResourceExceeded("max resource limit"); + result = Outcome.OutOfResource; + } else { + HandleProverError("Unexpected prover response: " + resp.ToString()); + } + break; default: HandleProverError("Unexpected prover response: " + resp.ToString()); break; @@ -1998,10 +2037,27 @@ result = Outcome.OutOfMemory; Process.NeedsRestart = true; break; - case "timeout": case "canceled": + case "timeout": currentErrorHandler.OnResourceExceeded("timeout"); result = Outcome.TimeOut; break; + case "canceled": + // both timeout and max resource limit are reported as + // canceled in version 4.4.1 + if (this.options.TimeLimit > 0) { + currentErrorHandler.OnResourceExceeded("timeout"); + result = Outcome.TimeOut; + } else { + currentErrorHandler.OnResourceExceeded("max resource limit"); + result = Outcome.OutOfResource; + } + break; + case "max. resource limit exceeded": + case "resource limits reached": + case "(resource limits reached)": + currentErrorHandler.OnResourceExceeded("max resource limit"); + result = Outcome.OutOfResource; + break; default: break; } @@ -2075,8 +2131,7 @@ // verification condition private readonly List!*/> Axioms = new List(); private bool AxiomsAreSetup = false; - - + private bool HasReset = false; // similarly, a list of function/predicate declarations @@ -2165,22 +2220,40 @@ throw new NotImplementedException(); } - public override void Assert(VCExpr vc, bool polarity, bool isSoft = false, int weight = 1) + public override void Assert(VCExpr vc, bool polarity, bool isSoft = false, int weight = 1, string name = null) { OptimizationRequests.Clear(); string assert = "assert"; - if (options.Solver == SolverKind.Z3 && isSoft) { + if (options.Solver == SolverKind.Z3 && isSoft) + { assert += "-soft"; } var expr = polarity ? VCExpr2String(vc, 1) : "(not\n" + VCExpr2String(vc, 1) + "\n)"; - if (options.Solver == SolverKind.Z3 && isSoft) { + if (options.Solver == SolverKind.Z3 && isSoft) + { expr += " :weight " + weight; } + if (name != null) + { + expr = "(! " + expr + ":named " + name + ")"; + } AssertAxioms(); SendThisVC("(" + assert + " " + expr + ")"); SendOptimizationRequests(); } + public override List UnsatCore() + { + SendThisVC("(get-unsat-core)"); + var resp = Process.GetProverResponse().ToString(); + if (resp == "" || resp == "()") + return null; + if (resp[0] == '(') + resp = resp.Substring(1, resp.Length - 2); + var ucore = resp.Split(' ').ToList(); + return ucore; + } + public override void DefineMacro(Macro f, VCExpr vc) { DeclCollector.AddFunction(f); string printedName = Namer.GetQuotedName(f, f.Name); @@ -2210,14 +2283,140 @@ public override void SetTimeOut(int ms) { - if (options.Solver == SolverKind.Z3) { - var name = Z3.SetTimeoutOption(); - var value = ms.ToString(); - options.TimeLimit = ms; - options.SmtOptions.RemoveAll(ov => ov.Option == name); - options.AddSmtOption(name, value); - SendThisVC(string.Format("(set-option :{0} {1})", name, value)); - } + if (ms < 0) { + throw new ArgumentOutOfRangeException ("ms must be >= 0"); + } + options.TimeLimit = ms; + } + + public override void SetRlimit(int limit) + { + if (options.Solver == SolverKind.Z3) { + var name = Z3.SetRlimitOption(); + if (name != "") { + var value = limit.ToString(); + options.ResourceLimit = limit; + options.SmtOptions.RemoveAll(ov => ov.Option == name); + options.AddSmtOption(name, value); + SendThisVC(string.Format("(set-option :{0} {1})", name, value)); + } + } + } + + object ParseValueFromProver(SExpr expr) { + return expr.ToString().Replace(" ","").Replace("(","").Replace(")",""); + } + + SExpr ReduceLet(SExpr expr) { + if (expr.Name != "let") + return expr; + + var bindings = expr.Arguments[0].Arguments; + var subexpr = expr.Arguments[1]; + + var dict = new Dictionary(); + foreach (var binding in bindings) { + Contract.Assert(binding.ArgCount == 1); + dict.Add(binding.Name, binding.Arguments[0]); + } + + Contract.Assert(!dict.ContainsKey(subexpr.Name)); + + var workList = new Stack(); + workList.Push(subexpr); + while (workList.Count > 0) { + var curr = workList.Pop(); + for (int i = 0; i < curr.ArgCount; i++) { + var arg = curr.Arguments[i]; + if (dict.ContainsKey(arg.Name)) + curr.Arguments[i] = dict[arg.Name]; + else + workList.Push(arg); + } + } + return subexpr; + } + + object GetArrayFromProverResponse(SExpr resp) { + resp = ReduceLet(resp); + var dict = GetArrayFromArrayExpr(resp); + if (dict == null) dict = GetArrayFromProverLambdaExpr(resp); + if (dict == null) return null; + var str = new StringWriter(); + str.Write("{"); + foreach (var entry in dict) + if (entry.Key != "*") + str.Write("\"{0}\":{1},", entry.Key, entry.Value); + if (dict.ContainsKey("*")) + str.Write("\"*\":{0}", dict["*"]); + str.Write("}"); + return str.ToString(); + } + + Dictionary GetArrayFromProverLambdaExpr(SExpr resp) { + var dict = new Dictionary(); + if (resp.Name == "lambda" && resp.ArgCount == 2) { + + // TODO: multiple indices, as (lambda (() (x!1 Int) (x!2 Int)) ...) + var indexVar = resp.Arguments[0].Arguments[0].Name; + + var cases = resp.Arguments[1]; + while (cases != null) { + if (cases.Name == "ite") { + var condition = cases.Arguments[0]; + var positive = cases.Arguments[1]; + var negative = cases.Arguments[2]; + + // TODO: multiple index conditions, as (and (= x!1 5) (= x!2 3)) + // TODO: nested arrays, as (ite (...) (_ as-array k!5) (_ as-array k!3)) + + if (condition.Name != "=") + throw new VCExprEvaluationException(); + + if (condition.Arguments[0].Name != indexVar) + throw new VCExprEvaluationException(); + + var index = ParseValueFromProver(condition.Arguments[1]); + var value = ParseValueFromProver(positive); + + dict.Add(index.ToString(), value); + + cases = negative; + + } else if (cases.IsId) { + var value = ParseValueFromProver(cases); + dict.Add("*", value); + cases = null; + + } else { + throw new VCExprEvaluationException(); + } + } + } + return dict.Count > 0 ? dict : null; + } + + Dictionary GetArrayFromArrayExpr(SExpr resp) { + var dict = new Dictionary(); + var curr = resp; + while (curr != null) { + if (curr.Name == "store") { + var ary = curr.Arguments[0]; + var indices = curr.Arguments.Skip(1).Take(curr.ArgCount - 2).Select(ParseValueFromProver).ToArray(); + var val = curr.Arguments[curr.ArgCount - 1]; + dict.Add(String.Join(",", indices), ParseValueFromProver(val)); + curr = ary; + + } else if (curr.Name == "" && curr.ArgCount == 2 && curr.Arguments[0].Name == "as") { + var val = curr.Arguments[1]; + dict.Add("*", ParseValueFromProver(val)); + curr = null; + + } else { + return null; + } + } + return dict.Count > 0 ? dict : null; } public override object Evaluate(VCExpr expr) @@ -2249,6 +2448,29 @@ if (resp.Name == "_" && resp.ArgCount == 2 && resp.Arguments[0].Name.StartsWith("bv")) // bitvector return new BvConst(Microsoft.Basetypes.BigNum.FromString(resp.Arguments[0].Name.Substring("bv".Length)), int.Parse(resp.Arguments[1].Name)); + if (resp.Name == "fp" && resp.ArgCount == 3) + { + Func getBvVal = e => BigInteger.Parse(e.Arguments[0].Name.Substring("bv".Length)); + Func getBvSize = e => int.Parse(e.Arguments[1].ToString()); + bool isNeg = getBvVal(resp.Arguments[0]).IsOne; + var expExpr = resp.Arguments[1]; + var sigExpr = resp.Arguments[2]; + BigInteger exp = getBvVal(expExpr); + int expSize = getBvSize(expExpr); + BigInteger sig = getBvVal(sigExpr); + int sigSize = getBvSize(sigExpr)+1; + return new Basetypes.BigFloat(isNeg, sig, exp, sigSize, expSize); + } + if (resp.Name == "_" && resp.ArgCount == 3) + { + String specialValue = resp.Arguments[0].ToString(); + int expSize = int.Parse(resp.Arguments[1].ToString()); + int sigSize = int.Parse(resp.Arguments[2].ToString()); + return new Basetypes.BigFloat(specialValue, sigSize, expSize); + } + var ary = GetArrayFromProverResponse(resp); + if (ary != null) + return ary; if (resp.ArgCount != 0) throw new VCExprEvaluationException(); if (expr.Type.Equals(Boogie.Type.Bool)) @@ -2483,10 +2705,26 @@ Process.NeedsRestart = true; break; case "timeout": - case "canceled": currentErrorHandler.OnResourceExceeded("timeout"); result = Outcome.TimeOut; break; + case "canceled": + // both timeout and max resource limit are reported as + // canceled in version 4.4.1 + if (this.options.TimeLimit > 0) { + currentErrorHandler.OnResourceExceeded("timeout"); + result = Outcome.TimeOut; + } else { + currentErrorHandler.OnResourceExceeded("max resource limit"); + result = Outcome.OutOfResource; + } + break; + case "max. resource limit exceeded": + case "resource limits reached": + case "(resource limits reached)": + currentErrorHandler.OnResourceExceeded("max resource limit"); + result = Outcome.OutOfResource; + break; default: break; } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLib.csproj boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLib.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLib.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLib.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -1,4 +1,4 @@ - + Debug @@ -10,7 +10,7 @@ Properties Microsoft.Boogie.SMTLib Provers.SMTLib - v4.0 + v4.5 512 1 true @@ -34,7 +34,7 @@ false false true - Client + true @@ -74,6 +74,7 @@ Full %28none%29 AllRules.ruleset + false pdbonly @@ -83,6 +84,7 @@ prompt 4 AllRules.ruleset + false true @@ -99,6 +101,7 @@ true 4 false + false true @@ -141,6 +144,7 @@ 0 4 false + false true @@ -150,6 +154,7 @@ AnyCPU prompt AllRules.ruleset + false @@ -157,6 +162,7 @@ 3.5 + @@ -234,4 +240,4 @@ --> - + \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLibLineariser.cs boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLibLineariser.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLibLineariser.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLibLineariser.cs 2019-10-25 18:17:05.000000000 +0000 @@ -121,7 +121,7 @@ } sb.Append(']'); TypeToStringHelper(m.Result, sb); - } else if (t.IsBool || t.IsInt || t.IsReal || t.IsFloat || t.IsBv) { + } else if (t.IsBool || t.IsInt || t.IsReal || t.IsFloat || t.IsBv || t.IsRMode) { sb.Append(TypeToString(t)); } else { System.IO.StringWriter buffer = new System.IO.StringWriter(); @@ -147,8 +147,10 @@ return "Real"; else if (t.IsFloat) return "(_ FloatingPoint " + t.FloatExponent + " " + t.FloatSignificand + ")"; - else if (t.IsBv) { + else if (t.IsBv) return "(_ BitVec " + t.BvBits + ")"; + else if (t.IsRMode) { + return "RoundingMode"; } else { StringBuilder sb = new StringBuilder(); TypeToStringHelper(t, sb); @@ -211,6 +213,11 @@ BigFloat lit = ((VCExprFloatLit)node).Val; wr.Write("(" + lit.ToBVString() + ")"); } + else if (node is VCExprRModeLit) + { + RoundingMode lit = ((VCExprRModeLit)node).Val; + wr.Write(lit.ToString()); + } else { Contract.Assert(false); throw new cce.UnreachableException(); @@ -603,7 +610,7 @@ } if(!options.LabelsBelowQuantifiers) - wr.Write("({0} {1} ", op.pos ? "and" : "or", SMTLibNamer.QuoteId(SMTLibNamer.LabelVar(op.label))); + wr.Write("({0} {1} ", op.pos ? "and" : "or", SMTLibNamer.QuoteId(ExprLineariser.Namer.LabelVar(op.label))); ExprLineariser.Linearise(node[0], options); @@ -612,7 +619,7 @@ wr.Write(")"); if (CommandLineOptions.Clo.UseLabels) - wr.Write(" :{0} {1})", op.pos ? "lblpos" : "lblneg", SMTLibNamer.QuoteId(op.label)); + wr.Write(" :{0} {1})", op.pos ? "lblpos" : "lblneg", SMTLibNamer.QuoteId(ExprLineariser.Namer.LabelName(op.label))); return true; } @@ -661,24 +668,6 @@ return true; } - public bool VisitFloatRemOp(VCExprNAry node, LineariserOptions options) - { - WriteApplication("fp.rem RNE", node, options); - return true; - } - - public bool VisitFloatMinOp(VCExprNAry node, LineariserOptions options) - { - WriteApplication("fp.min", node, options); - return true; - } - - public bool VisitFloatMaxOp(VCExprNAry node, LineariserOptions options) - { - WriteApplication("fp.max", node, options); - return true; - } - public bool VisitFloatLeqOp(VCExprNAry node, LineariserOptions options) { WriteApplication("fp.leq", node, options); @@ -709,6 +698,14 @@ return true; } + public bool VisitFloatNeqOp(VCExprNAry node, LineariserOptions options) + { + wr.Write("(not "); + VisitFloatEqOp(node, options); + wr.Write(")"); + return true; + } + static char[] hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; public bool VisitBvOp(VCExprNAry node, LineariserOptions options) { diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLibNamer.cs boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLibNamer.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLibNamer.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLibNamer.cs 2019-10-25 18:17:05.000000000 +0000 @@ -52,12 +52,14 @@ "real_pow", "UOrdering2", "UOrdering3", // Floating point (final draft SMTLIB-v2.5) "NaN", - "roundNearestTiesToEven", "roundNearestTiesToAway", "roundTowardPositive", "roundTowardNegative", "roundTowardZero", - "RNE", "RNA", "RTP", "RTN", "RTZ", "fp.abs", "fp.neg", "fp.add", "fp.sub", "fp.mul", "fp.div", "fp.fma", "fp.sqrt", "fp.rem", "fp.roundToIntegral", "fp.min", "fp.max", "fp.leq", "fp.lt", "fp.geq", "fp.gt", "fp.eq", "fp.isNormal", "fp.isSubnormal", "fp.isZero", "fp.isInfinite", "fp.isNaN", "fp.isNegative", "fp.isPositive", "fp", "fp.to_ubv", "fp.to_sbv", "to_fp", + // Rounding mode + "rmode", + "roundNearestTiesToEven", "roundNearestTiesToAway", "roundTowardPositive", "roundTowardNegative", "roundTowardZero", + "RNE", "RNA", "RTP", "RTN", "RTZ", }; static HashSet reservedSmtWords; @@ -118,9 +120,50 @@ return s; } - public static string LabelVar(string s) + public IDictionary/*!*/ LabelCounters; // Absy id -> local id + public IDictionary/*!*/ CounterToLabels; // local id -> Absy id + private int CurrentLabelId; + + public override void ResetLabelCount() + { + LabelCounters = new Dictionary(); + CounterToLabels = new Dictionary(); + CurrentLabelId = 0; + } + + public override string LabelVar(string s) + { + return "%lbl%" + LabelName(s); + } + + public override string LabelName(string s) + { + + if (s[0] == '+' || s[0] == '@') { + return s[0] + AbsyIndexToLocalIndex(s.Substring(1)); + } else { + return AbsyIndexToLocalIndex(s); + } + } + + private string AbsyIndexToLocalIndex (string s) { + string counter; + if (!LabelCounters.TryGetValue(s, out counter)) { + counter = CurrentLabelId.ToString(); + CurrentLabelId++; + LabelCounters[s] = counter; + CounterToLabels[counter] = s; + } + return counter; + } + + public override string AbsyLabel(string s) { - return "%lbl%" + s; + if (s[0] == '+' || s[0] == '@') { + return s[0] + cce.NonNull(CounterToLabels[s.Substring(1)]); + } else { + return cce.NonNull(CounterToLabels[s.Substring(1)]); + } } public static string QuoteId(string s) @@ -142,6 +185,24 @@ { this.Spacer = "@@"; InitSymbolLists(); + LabelCounters = new Dictionary(); + CounterToLabels = new Dictionary(); + CurrentLabelId = 0; + } + + private SMTLibNamer(SMTLibNamer namer) : base(namer) { } + + public override object Clone() + { + return new SMTLibNamer(this); + } + + public override void Reset() + { + LabelCounters.Clear(); + CounterToLabels.Clear(); + CurrentLabelId = 0; + base.Reset(); } } } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLib-NetCore.csproj boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLib-NetCore.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLib-NetCore.csproj 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLib-NetCore.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,34 @@ + + + + Library + Provers.SMTLib + netcoreapp2.2 + COREFX_SUBSET + false + + + + + + + + + + + + + + + false + + + + + version.cs + + + + + + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLibProcess.cs boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLibProcess.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLibProcess.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLibProcess.cs 2019-10-25 18:17:05.000000000 +0000 @@ -154,7 +154,10 @@ var resp = exprs[0]; if (resp.Name == "error") { if (resp.Arguments.Length == 1 && resp.Arguments[0].IsId) - HandleError(resp.Arguments[0].Name); + if (resp.Arguments[0].Name.Contains("max. resource limit exceeded")) + return resp; + else + HandleError(resp.Arguments[0].Name); else HandleError(resp.ToString()); } else if (resp.Name == "progress") { diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLibProverOptions.cs boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLibProverOptions.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/SMTLibProverOptions.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/SMTLibProverOptions.cs 2019-10-25 18:17:05.000000000 +0000 @@ -43,7 +43,7 @@ // Z3 specific (at the moment; some of them make sense also for other provers) public string Inspector = null; public bool OptimizeForBv = false; - public bool SMTLib2Model = false; + public bool SMTLib2Model = false; public bool ProduceModel() { return !CommandLineOptions.Clo.UseLabels || CommandLineOptions.Clo.ExplainHoudini || CommandLineOptions.Clo.UseProverEvaluate || @@ -94,7 +94,7 @@ case "CVC4": case "cvc4": Solver = SolverKind.CVC4; - if (Logic.Equals("")) Logic = "ALL_SUPPORTED"; + if (Logic.Equals("")) Logic = "ALL_SUPPORTED"; break; default: ReportError("Invalid SOLVER value; must be 'Z3' or 'CVC4'"); @@ -118,7 +118,7 @@ ParseBool(opt, "USE_WEIGHTS", ref UseWeights) || ParseString(opt, "INSPECTOR", ref Inspector) || ParseBool(opt, "OPTIMIZE_FOR_BV", ref OptimizeForBv) || - ParseBool(opt, "SMTLIB2_MODEL", ref SMTLib2Model) || + ParseBool(opt, "SMTLIB2_MODEL", ref SMTLib2Model) || ParseString(opt, "LOGIC", ref Logic) || base.Parse(opt); } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/TypeDeclCollector.cs boogie-2.4.1+dfsg/Source/Provers/SMTLib/TypeDeclCollector.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/TypeDeclCollector.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/TypeDeclCollector.cs 2019-10-25 18:17:05.000000000 +0000 @@ -18,7 +18,7 @@ public class TypeDeclCollector : BoundVarTraversingVCExprVisitor { - private readonly UniqueNamer Namer; + private UniqueNamer Namer; private readonly SMTLibProverOptions Options; private HashSet/*!*/ RegisteredRelations = new HashSet(); @@ -148,6 +148,11 @@ _KnownLBL.Pop(); } + public void SetNamer(UniqueNamer namer) + { + Namer = namer; + } + public List!*/> AllDeclarations { get { Contract.Ensures(cce.NonNullElements(Contract.Result>() )); @@ -249,7 +254,7 @@ var lab = node.Op as VCExprLabelOp; if (lab != null && !KnownLBL.Contains(lab.label)) { KnownLBL.Add(lab.label); - var name = SMTLibNamer.QuoteId(SMTLibNamer.LabelVar(lab.label)); + var name = SMTLibNamer.QuoteId(Namer.LabelVar(lab.label)); AddDeclaration("(declare-fun " + name + " () Bool)"); } } @@ -311,7 +316,7 @@ return; } - if (type.IsBool || type.IsInt || type.IsReal || type.IsBv || type.IsFloat) + if (type.IsBool || type.IsInt || type.IsReal || type.IsBv || type.IsFloat || type.IsRMode) return; CtorType ctorType = type as CtorType; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/Z3.cs boogie-2.4.1+dfsg/Source/Provers/SMTLib/Z3.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/SMTLib/Z3.cs 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/SMTLib/Z3.cs 2019-10-25 18:17:05.000000000 +0000 @@ -26,6 +26,7 @@ } public static string ExecutablePath() + // throws ProverException, System.IO.FileNotFoundException; { if (_proverPath == null) FindExecutable(); @@ -186,11 +187,23 @@ return "SOFT_TIMEOUT"; } + public static string SetRlimitOption() + { + int major, minor; + GetVersion(out major, out minor); + if (major > 4 || major == 4 && minor >= 3) + return "RLIMIT"; + else + // not sure what is for "rlimit" in earlier versions. + return ""; + } + // options that work only on the command line static string[] commandLineOnly = { "TRACE", "PROOF_MODE" }; public static void SetupOptions(SMTLibProverOptions options) + // throws ProverException, System.IO.FileNotFoundException; { FindExecutable(); int major, minor; @@ -198,7 +211,7 @@ if (major > 4 || major == 4 && minor >= 3) { - bool fp = false; // CommandLineOptions.Clo.FixedPointEngine != null; + bool fp = false; // CommandLineOptions.Clo.FixedPointEngine != null; // don't bother with auto-config - it would disable explicit settings for eager threshold and so on if(!fp) options.AddWeakSmtOption("AUTO_CONFIG", "false"); @@ -258,14 +271,27 @@ options.AddWeakSmtOption("TYPE_CHECK", "true"); options.AddWeakSmtOption("smt.BV.REFLECT", "true"); + if (major > 4 || (major == 4 && minor >= 8)) { + // {:captureState} does not work with compressed models + options.AddWeakSmtOption("model_compress", "false"); + } + if (options.TimeLimit > 0) { options.AddWeakSmtOption("TIMEOUT", options.TimeLimit.ToString()); - options.AddWeakSmtOption("fixedpoint.TIMEOUT", options.TimeLimit.ToString()); + if (major == 4 && minor < 8) + { + options.AddWeakSmtOption("fixedpoint.TIMEOUT", options.TimeLimit.ToString()); + } // This kills the Z3 *instance* after the specified time, not a particular query, so we cannot use it. // options.AddSolverArgument("/T:" + (options.TimeLimit + 1000) / 1000); } + if (options.ResourceLimit > 0) + { + options.AddWeakSmtOption("RLIMIT", options.ResourceLimit.ToString()); + } + if (options.Inspector != null) options.AddWeakSmtOption("PROGRESS_SAMPLING_FREQ", "200"); @@ -275,7 +301,7 @@ options.AddWeakSmtOption("smt.array.extensional", "false"); } - if (CommandLineOptions.Clo.PrintConjectures != null) + if (CommandLineOptions.Clo.PrintConjectures != null && major == 4 && minor < 8) { options.AddWeakSmtOption("fixedpoint.conjecture_file", CommandLineOptions.Clo.PrintConjectures + ".tmp"); } @@ -359,8 +385,8 @@ } - // KLM: don't add Z3 options here. The options are different in different Z3 versions. - // Add options in the above condition for the appropriate version. + // KLM: don't add Z3 options here. The options are different in different Z3 versions. + // Add options in the above condition for the appropriate version. // legacy option handling if (!CommandLineOptions.Clo.z3AtFlag) diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/TPTP/TPTP.csproj boogie-2.4.1+dfsg/Source/Provers/TPTP/TPTP.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/TPTP/TPTP.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/TPTP/TPTP.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,9 +10,9 @@ Properties Microsoft.Boogie.TPTP Provers.TPTP - v4.0 + v4.5 512 - Client + 0 @@ -23,6 +23,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -31,6 +32,7 @@ TRACE prompt 4 + false true @@ -71,6 +73,7 @@ Full Build 0 + false diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/Z3api/Z3api.csproj boogie-2.4.1+dfsg/Source/Provers/Z3api/Z3api.csproj --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Source/Provers/Z3api/Z3api.csproj 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Source/Provers/Z3api/Z3api.csproj 2019-10-25 18:17:05.000000000 +0000 @@ -10,7 +10,7 @@ Properties Microsoft.Boogie.Z3api Provers.Z3api - v4.0 + v4.5 512 @@ -33,7 +33,7 @@ false false true - Client + 0 @@ -45,6 +45,7 @@ prompt 4 AllRules.ruleset + false pdbonly @@ -54,6 +55,7 @@ prompt 4 AllRules.ruleset + false true @@ -68,6 +70,7 @@ prompt Migrated rules for Z3api.ruleset true + false true @@ -108,6 +111,7 @@ Full Build 0 + false MARK + call YieldGarbageCollect(tid); + call WaitForMutators(tid, nextPhase); + assert {:layer 98} MarkPhase(collectorPhase); + call MarkOuterLoop(tid); + assert {:layer 98} MarkPhase(collectorPhase); + call nextPhase := HandshakeCollector(tid); // MARK --> SWEEP + assert {:layer 98} SweepPhase(collectorPhase); + call YieldGarbageCollect(tid); + call WaitForMutators(tid, nextPhase); + assert {:layer 98} SweepPhase(collectorPhase); + assert {:layer 98} PhaseConsistent(collectorPhase, mutatorPhase); + call Sweep(tid); + call nextPhase := HandshakeCollector(tid); // SWEEP --> IDLE + call YieldGarbageCollect(tid); + } + yield; +} + +procedure {:yields} {:layer 100} YieldMarkBegin({:linear "tid"} tid:Tid) +requires {:layer 98,99,100} tid == GcTid; +requires {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +requires {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +requires {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memHi; +requires {:layer 100} Iso(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +requires {:layer 100} (forall x: int :: memAddr(x) ==> (toAbs[x] == nil <==> Unalloc(Color[x]))); +requires {:layer 100} (forall x: int :: memAddr(x) ==> !Black(Color[x])); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} collectorPhase == old(collectorPhase); +ensures {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +ensures {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); +ensures {:layer 100} Iso(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 100} (forall x: int :: memAddr(x) ==> (toAbs[x] == nil <==> Unalloc(Color[x]))); +ensures {:layer 100} (forall x: int :: memAddr(x) ==> !Black(Color[x])); +{ + yield; + assert {:layer 98,99,100} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); + assert {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + assert {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memHi; + assert {:layer 100} Iso(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + assert {:layer 100} (forall x: int :: memAddr(x) ==> (toAbs[x] == nil <==> Unalloc(Color[x]))); + assert {:layer 100} (forall x: int :: memAddr(x) ==> !Black(Color[x])); +} + +procedure {:yields} {:layer 100} YieldMark({:linear "tid"} tid:Tid) +requires {:layer 98,99,100} tid == GcTid; +requires {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +requires {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +requires {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; +requires {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} collectorPhase == old(collectorPhase); +ensures {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +ensures {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; +ensures {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 100} (forall x: int :: memAddr(x) && !Unalloc(old(Color)[x]) ==> !Unalloc(Color[x])); +ensures {:layer 100} (forall x: int :: memAddr(x) && !Unalloc(old(Color)[x]) && !White(old(Color)[x]) ==> !White(Color[x])); +{ + yield; + assert {:layer 98,99,100} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); + assert {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + assert {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; + assert {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + assert {:layer 100} (forall x: int :: memAddr(x) && !Unalloc(old(Color)[x]) ==> !Unalloc(Color[x])); + assert {:layer 100} (forall x: int :: memAddr(x) && !Unalloc(old(Color)[x]) && !White(old(Color)[x]) ==> !White(Color[x])); +} + +procedure {:yields} {:layer 100} YieldMarkEnd({:linear "tid"} tid:Tid) +requires {:layer 98,99,100} tid == GcTid; +requires {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +requires {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +requires {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; +requires {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +requires {:layer 100} (forall x: int :: memAddr(x) ==> !Gray(Color[x])); +requires {:layer 100} (forall i: int :: rootAddr(i) && memAddr(root[i]) ==> Black(Color[root[i]])); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} collectorPhase == old(collectorPhase); +ensures {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +ensures {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; +ensures {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 100} (forall x: int :: memAddr(x) ==> !Gray(Color[x])); +ensures {:layer 100} (forall i: int :: rootAddr(i) && memAddr(root[i]) ==> Black(Color[root[i]])); +{ + yield; + assert {:layer 98,99,100} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); + assert {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + assert {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; + assert {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + assert {:layer 100} (forall x: int :: memAddr(x) ==> !Gray(Color[x])); + assert {:layer 100} (forall i: int :: rootAddr(i) && memAddr(root[i]) ==> Black(Color[root[i]])); +} + +procedure {:yields} {:layer 100} MarkOuterLoop({:linear "tid"} tid:Tid) +requires {:layer 98,99,100} tid == GcTid; +requires {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +requires {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +requires {:layer 100} Iso(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +requires {:layer 100} (forall x: int :: memAddr(x) ==> (toAbs[x] == nil <==> Unalloc(Color[x]))); +requires {:layer 100} (forall x: int :: memAddr(x) ==> !Black(Color[x])); +requires {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memHi; +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} collectorPhase == old(collectorPhase); +ensures {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +ensures {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; +ensures {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 100} (forall x: int :: memAddr(x) ==> !Gray(Color[x])); +ensures {:layer 100} (forall i: int :: rootAddr(i) && memAddr(root[i]) ==> Black(Color[root[i]])); +{ + var canStop: bool; + + call YieldMarkBegin(tid); + call ResetSweepPtr(tid); + call YieldMark(tid); + while (true) + invariant {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + invariant {:layer 98} collectorPhase == old(collectorPhase); + invariant {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + invariant {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + invariant {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; + { + call canStop := CanMarkStop(tid); + if (canStop) + { + call YieldMarkEnd(tid); + return; + } + call MarkInnerLoop(tid); + } + call YieldMarkEnd(tid); +} + +procedure {:yields} {:layer 100} MarkInnerLoop({:linear "tid"} tid:Tid) +requires {:layer 98,99,100} tid == GcTid; +requires {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +requires {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +requires {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; +requires {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} collectorPhase == old(collectorPhase); +ensures {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +ensures {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; +ensures {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +{ + var nodeProcessed:int; + var fldIter: int; + var isEmpty: bool; + var child: int; + + call YieldMark(tid); + while (true) + invariant {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + invariant {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + invariant {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; + invariant {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + invariant {:layer 98} collectorPhase == old(collectorPhase); + { + call isEmpty, nodeProcessed := SET_Peek(tid); + if (isEmpty) { + break; + } + fldIter := 0; + while (fldIter < numFields) + invariant {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, nodeProcessed); + invariant {:layer 98} collectorPhase == old(collectorPhase); + invariant {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + invariant {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; + invariant {:layer 100} !Unalloc(Color[nodeProcessed]); + invariant {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + invariant {:layer 100} 0 <= fldIter && fldIter <= numFields; + invariant {:layer 100} (forall x: int :: 0 <= x && x < fldIter && memAddr(mem[nodeProcessed][x]) ==> !Unalloc(Color[mem[nodeProcessed][x]]) && !White(Color[mem[nodeProcessed][x]])); + { + call child := ReadFieldCollector(tid, nodeProcessed, fldIter); + if (memAddr(child)) + { + call SET_InsertIntoSetIfWhite(tid, nodeProcessed, child); + } + fldIter := fldIter + 1; + yield; + assert {:layer 98,99,100} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, nodeProcessed); + assert {:layer 98} collectorPhase == old(collectorPhase); + assert {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + assert {:layer 100} MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo; + assert {:layer 100} !Unalloc(Color[nodeProcessed]); + assert {:layer 100} MarkInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + assert {:layer 100} (forall x: int :: 0 <= x && x < fldIter && memAddr(mem[nodeProcessed][x]) ==> !Unalloc(Color[mem[nodeProcessed][x]]) && !White(Color[mem[nodeProcessed][x]])); + } + call SET_RemoveFromSet(tid, nodeProcessed); + call YieldMark(tid); + } + call YieldMark(tid); +} + +procedure {:atomic} {:layer 100} AtomicCanMarkStop({:linear "tid"} tid:Tid) returns (canStop: bool) +modifies Color; +{ + assert tid == GcTid; + havoc Color; + assume (forall u: int :: if memAddr(u) && White(old(Color)[u]) && (exists k: int :: rootAddr(k) && root[k] == u) then Color[u] == GRAY() else Color[u] == old(Color)[u]); + canStop := (forall v: int :: memAddr(v) ==> !Gray(Color[v])); +} + +procedure {:yields} {:layer 99} {:refines "AtomicCanMarkStop"} CanMarkStop({:linear "tid"} tid:Tid) returns (canStop: bool) +requires {:layer 98,99} tid == GcTid; +requires {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} collectorPhase == old(collectorPhase); +requires {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +ensures {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +{ + var i: int; + var o: int; + var {:layer 99} snapColor: [int]int; + + yield; + assert {:layer 98,99} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); + assert {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + call CollectorRootScanBarrierStart(tid); + yield; + assert {:layer 98,99} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); + assert {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier) && rootScanOn; + call snapColor := GhostReadColor99(); + call CollectorRootScanBarrierWait(tid); + + i := 0; + while (i < numRoots) + invariant {:terminates} {:layer 96,97,98,99} true; + invariant {:layer 99} Mutators == mutatorsInRootScanBarrier && rootScanOn; + invariant {:layer 99} 0 <= i && i <= numRoots; + invariant {:layer 99} Color == (lambda u: int :: if memAddr(u) && White(snapColor[u]) && (exists k: int :: 0 <= k && k < i && root[k] == u) then GRAY() else snapColor[u]); + invariant {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + invariant {:layer 98} collectorPhase == old(collectorPhase); + { + call o := ReadRootInRootScanBarrier(tid, i); + if (memAddr(o)) + { + call InsertIntoSetIfWhiteInRootScanBarrier(tid, o); + } + i := i + 1; + } + call canStop := NoGrayInRootScanBarrier(tid); + call CollectorRootScanBarrierEnd(tid); + yield; + assert {:layer 98,99} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); + assert {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +} + +procedure {:yields} {:layer 100} YieldSweepBegin({:linear "tid"} tid:Tid, localSweepPtr: int, fldIter: int, isInit: bool) +requires {:layer 98,99,100} tid == GcTid; +requires {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +requires {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +requires {:layer 98,100} SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); +requires {:layer 100} sweepPtr == memLo; +requires {:layer 100} !isInit ==> SweepInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +requires {:layer 100} isInit ==> SweepInvInit(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +requires {:layer 100} (forall i: int :: rootAddr(i) && memAddr(root[i]) ==> Black(Color[root[i]])); +requires {:layer 100} (forall x: int, f: fld :: memLo <= x && x < localSweepPtr && (Unalloc(Color[x]) || White(Color[x])) && fieldAddr(f) ==> mem[x][f] == x); +requires {:layer 100} (forall f: fld :: 0 <= f && f < fldIter ==> mem[localSweepPtr][f] == localSweepPtr); +requires {:layer 100} fldIter == 0 || White(Color[localSweepPtr]); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +ensures {:layer 98,100} SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); +ensures {:layer 100} !isInit ==> SweepInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 100} isInit ==> SweepInvInit(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 100} (forall i: int :: rootAddr(i) && memAddr(root[i]) ==> Black(Color[root[i]])); +ensures {:layer 100} sweepPtr == memLo; +ensures {:layer 100} (forall x: int :: memAddr(x) && !Unalloc(old(Color)[x]) ==> old(Color)[x] == Color[x]); +ensures {:layer 100} (forall x: int, f: fld :: memLo <= x && x < localSweepPtr && (Unalloc(Color[x]) || White(Color[x])) && fieldAddr(f) ==> mem[x][f] == x); +ensures {:layer 100} (forall f: fld :: 0 <= f && f < fldIter ==> mem[localSweepPtr][f] == localSweepPtr); +ensures {:layer 100} fldIter == 0 || White(Color[localSweepPtr]); +{ + yield; + assert {:layer 98,99,100} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + assert {:layer 98,100} SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); + assert {:layer 100} sweepPtr == memLo; + assert {:layer 100} !isInit ==> SweepInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + assert {:layer 100} isInit ==> SweepInvInit(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + assert {:layer 100} (forall i: int :: rootAddr(i) && memAddr(root[i]) ==> Black(Color[root[i]])); + assert {:layer 100} (forall x: int :: memAddr(x) && !Unalloc(old(Color)[x]) ==> old(Color)[x] == Color[x]); + assert {:layer 100} (forall x: int, f: fld :: memLo <= x && x < localSweepPtr && (Unalloc(Color[x]) || White(Color[x])) && fieldAddr(f) ==> mem[x][f] == x); + assert {:layer 100} (forall f: fld :: 0 <= f && f < fldIter ==> mem[localSweepPtr][f] == localSweepPtr); + assert {:layer 100} fldIter == 0 || White(Color[localSweepPtr]); +} + +procedure {:yields} {:layer 100} YieldSweepEnd({:linear "tid"} tid:Tid) +requires {:layer 98,99,100} tid == GcTid; +requires {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +requires {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +requires {:layer 98,100} SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); +requires {:layer 100} sweepPtr == memHi; +requires {:layer 100} SweepInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +requires {:layer 100} (forall x: int :: memAddr(x) ==> !Black(Color[x])); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +ensures {:layer 98,100} SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); +ensures {:layer 100} SweepInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 100} (forall x: int :: memAddr(x) ==> !Black(Color[x])); +ensures {:layer 100} sweepPtr == memHi; +{ + yield; + assert {:layer 98,99,100} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); + assert {:layer 98,100} SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); + assert {:layer 100} SweepInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); + assert {:layer 100} (forall x: int :: memAddr(x) ==> !Black(Color[x])); + assert {:layer 100} sweepPtr == memHi; +} + +procedure {:yields} {:layer 100} Sweep({:linear "tid"} tid:Tid) +requires {:layer 98,99,100} tid == GcTid; +requires {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +requires {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +requires {:layer 98,100} SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); +requires {:layer 100} sweepPtr == memLo; +requires {:layer 100} (forall i: int :: rootAddr(i) && memAddr(root[i]) ==> Black(Color[root[i]])); +requires {:layer 100} SweepInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 99} RootScanBarrierInv(mutatorsInRootScanBarrier, rootScanBarrier); +ensures {:layer 100} SweepInv(root, rootAbs, mem, memAbs, Color, toAbs, allocSet); +ensures {:layer 100} (forall x: int :: memAddr(x) ==> !Black(Color[x])); +ensures {:layer 100} sweepPtr == memHi; +ensures {:layer 98,100} SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); +{ + var localSweepPtr: int; + var {:layer 100} snapColor: [int]int; + + localSweepPtr := memLo; + call YieldSweepBegin(tid, localSweepPtr, 0, false); + call ClearToAbsWhite(tid); + call YieldSweepBegin(tid, localSweepPtr, 0, true); + + call snapColor := GhostReadColor100(); + while (localSweepPtr < memHi) + invariant {:terminates} {:layer 97,98,99,100} true; + invariant {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + invariant {:layer 98,100} SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); + invariant {:layer 100} localSweepPtr == sweepPtr && memLo <= sweepPtr && sweepPtr <= memHi; + invariant {:layer 100} (forall i: int :: rootAddr(i) && memAddr(root[i]) ==> Black(snapColor[root[i]])); + invariant {:layer 100} SweepInvInit(root, rootAbs, mem, memAbs, snapColor, toAbs, allocSet); + invariant {:layer 100} (forall i:int:: memAddr(i) ==> if sweepPtr <= i then Color[i] == snapColor[i] else if Black(snapColor[i]) then White(Color[i]) else Unalloc(Color[i])); + { + call SweepNext(tid); + localSweepPtr := localSweepPtr + 1; + } + call YieldSweepEnd(tid); +} + +procedure {:atomic} {:layer 100} AtomicWriteFieldRaw({:linear "tid"} tid:Tid, x: idx, f: fld, y: idx) +modifies memAbs, mem; +{ + assert mutatorTidWhole(tid) && rootAddr(x) && tidOwns(tid, x) && fieldAddr(f) && rootAddr(y) && tidOwns(tid, y) && memAddr(root[x]) && toAbs[root[x]] != nil && memAddrAbs(rootAbs[x]); + memAbs[rootAbs[x]][f] := rootAbs[y]; + mem[root[x]][f] := root[y]; +} + +procedure {:yields} {:layer 99} {:refines "AtomicWriteFieldRaw"} WriteFieldRaw({:linear "tid"} tid:Tid, x: idx, f: fld, y: idx) +requires {:layer 98} mutatorTidWhole(tid); +{ + var valx: int; + var valy: int; + + yield; + assert {:layer 98} mutatorTidWhole(tid); + call valx := ReadRoot(tid, x); + call valy := ReadRoot(tid, y); + call WriteFieldGeneral(tid, valx, f, valy); + call SetMemAbs1(x, f, y); + yield; + assert {:layer 98} mutatorTidWhole(tid); +} + +procedure {:atomic} {:layer 100} AtomicReadFieldRaw({:linear "tid"} tid:Tid, x: idx, f: fld, y: idx) +modifies rootAbs, root; +{ + assert mutatorTidWhole(tid) && rootAddr(x) && tidOwns(tid, x) && fieldAddr(f) && rootAddr(y) && tidOwns(tid, y) && memAddr(root[x]) && toAbs[root[x]] != nil && memAddrAbs(rootAbs[x]); + rootAbs[y] := memAbs[rootAbs[x]][f]; + root[y] := mem[root[x]][f]; +} + +procedure {:yields} {:layer 99} {:refines "AtomicReadFieldRaw"} ReadFieldRaw({:linear "tid"} tid:Tid, x: idx, f: fld, y: idx) +{ + var valx: int; + var valy: int; + + yield; + call valx := ReadRoot(tid, x); + call valy := ReadFieldGeneral(tid, valx, f); + call WriteRoot(tid, y, valy); + call SetRootAbs1(x, f, y); + yield; +} + +procedure {:atomic} {:layer 100} AtomicEqRaw({:linear "tid"} tid:Tid, x: idx, y:idx) returns (isEqual:bool) +{ assert mutatorTidWhole(tid) && rootAddr(x) && tidOwns(tid, x) && rootAddr(y) && tidOwns(tid, y); isEqual := root[x] == root[y]; } + +procedure {:yields} {:layer 99} {:refines "AtomicEqRaw"} EqRaw({:linear "tid"} tid:Tid, x: idx, y:idx) returns (isEqual:bool) +{ + var vx:int; + var vy:int; + + yield; + call vx := ReadRoot(tid, x); + call vy := ReadRoot(tid, y); + isEqual := vx == vy; + yield; +} + +procedure {:atomic} {:layer 100} AtomicAllocRaw({:linear "tid"} tid:Tid, y:idx) returns (ptr: int, absPtr: obj) +modifies allocSet, rootAbs, root, toAbs, memAbs, Color, mem; +{ + assert mutatorTidWhole(tid) && rootAddr(y) && tidOwns(tid, y); + assert (forall x: int, f: fld :: memAddr(x) && Unalloc(Color[x]) ==> toAbs[x] == nil); + assume(memAddr(ptr) && Unalloc(Color[ptr])); + assume(memAddrAbs(absPtr) && !allocSet[absPtr] && absPtr != nil); + allocSet[absPtr] := true; + rootAbs[y] := absPtr; + root[y] := ptr; + toAbs[ptr] := absPtr; + memAbs[absPtr] := (lambda z: int :: if (fieldAddr(z)) then absPtr else memAbs[absPtr][z]); + Color[ptr] := if sweepPtr <= ptr then BLACK() else WHITE(); + mem[ptr] := (lambda z: int :: if (fieldAddr(z)) then ptr else mem[ptr][z]); +} + +procedure {:yields} {:layer 99} {:refines "AtomicAllocRaw"} AllocRaw({:linear "tid"} tid:Tid, y:idx) returns (ptr: int, absPtr: obj) +{ + yield; + call absPtr := PrimitiveFindFreePtrAbs(); + call ptr := FindFreePtr(tid, absPtr); + call WriteRoot(tid, y, ptr); + call SetMemAbs2(absPtr); + call SetRootAbs2(y, absPtr); + yield; +} + +procedure {:atomic} {:layer 99} AtomicFindFreePtr({:linear "tid"} tid: Tid, absPtr: obj) returns (ptr: int) +modifies Color, toAbs, mem; +{ + assert mutatorTidWhole(tid); + assert (forall x: int, f: fld :: memAddr(x) && Unalloc(Color[x]) ==> toAbs[x] == nil); + assume (memAddr(ptr) && Unalloc(Color[ptr])); + Color[ptr] := if sweepPtr <= ptr then BLACK() else WHITE(); + toAbs[ptr] := absPtr; + mem[ptr] := (lambda z: int :: if (fieldAddr(z)) then ptr else mem[ptr][z]); +} + +procedure {:yields} {:layer 98} {:refines "AtomicFindFreePtr"} FindFreePtr({:linear "tid"} tid: Tid, absPtr: obj) returns (ptr: int) +{ + var iter: int; + var spaceFound: bool; + + yield; + spaceFound := false; + while (true) + invariant {:layer 98} !spaceFound; + invariant {:layer 98} (forall x: int, f: fld :: memAddr(x) && Unalloc(Color[x]) ==> toAbs[x] == nil); + { + iter := memLo; + while (iter < memHi) + invariant {:layer 98} !spaceFound; + invariant {:layer 98} memLo <= iter && iter <= memHi; + invariant {:layer 98} memAddr(iter) && Unalloc(Color[iter]) ==> toAbs[iter] == nil; + { + call spaceFound := AllocIfPtrFree(tid, iter, absPtr); + if (spaceFound) + { + ptr := iter; + break; + } + else + { + iter := iter + 1; + } + yield; + } + if (spaceFound) + { + break; + } + yield; + } + yield; +} + +procedure{:atomic} {:layer 100} AtomicWriteBarrier({:linear "tid"} tid:Tid, y:idx) +modifies Color; +{ + var val:int; + assert mutatorTidWhole(tid) && rootAddr(y) && tidOwns(tid, y); + val := root[y]; + if (MarkPhase(mutatorPhase[i#Tid(tid)]) && memAddr(val) && White(Color[val])) { + Color[val] := GRAY(); + } +} + +procedure{:yields} {:layer 99} {:refines "AtomicWriteBarrier"} WriteBarrier({:linear "tid"} tid:Tid, y:idx) +requires {:layer 98} mutatorTidWhole(tid); +{ + var phase: int; + var rootVal: int; + + yield; + assert {:layer 98} mutatorTidWhole(tid); + call rootVal := ReadRoot(tid, y); + if (memAddr(rootVal)) + { + call phase := ReadMutatorPhase(tid); + if (MarkPhase(phase)) + { + call SET_InsertIntoSetIfWhiteByMutator(tid, rootVal); + } + } + yield; + assert {:layer 98} mutatorTidWhole(tid); +} + +procedure {:atomic} {:layer 99} AtomicSET_InsertIntoSetIfWhiteByMutator({:linear "tid"} tid:Tid, memLocal:int) +modifies Color; +{ + assert mutatorTidWhole(tid) && memAddr(memLocal) && MarkPhase(mutatorPhase[i#Tid(tid)]); + if (White(Color[memLocal])) { + Color[memLocal] := GRAY(); + } +} + +procedure {:yields} {:layer 98} {:refines "AtomicSET_InsertIntoSetIfWhiteByMutator"} SET_InsertIntoSetIfWhiteByMutator({:linear "tid"} tid:Tid, memLocal:int) +requires {:layer 98} mutatorTidWhole(tid) && MarkPhase(mutatorPhase[i#Tid(tid)]); +ensures {:layer 98} MarkPhase(mutatorPhase[i#Tid(tid)]); +{ + var color:int; + + yield; + assert {:layer 98} mutatorTidWhole(tid) && MarkPhase(mutatorPhase[i#Tid(tid)]); + assert {:layer 98} Color[memLocal] >= old(Color)[memLocal]; + + call color := ReadColorByMutator3(tid, memLocal); + if (!White(color)) + { + yield; + assert {:layer 98} mutatorTidWhole(tid) && MarkPhase(mutatorPhase[i#Tid(tid)]); + assert {:layer 98} Color[memLocal] >= old(Color)[memLocal]; + return; + } + + yield; + assert {:layer 98} mutatorTidWhole(tid) && MarkPhase(mutatorPhase[i#Tid(tid)]); + assert {:layer 98} Color[memLocal] >= old(Color)[memLocal]; + + call MsPushByMutator(tid, memLocal); + assert {:layer 98} MST(MarkStackPtr-1); + yield; + assert {:layer 98} mutatorTidWhole(tid) && MarkPhase(mutatorPhase[i#Tid(tid)]); +} + +procedure {:left} {:layer 99} AtomicNoGrayInRootScanBarrier({:linear "tid"} tid:Tid) returns (noGray: bool) +{ + assert tid == GcTid && rootScanOn && mutatorsInRootScanBarrier == Mutators; + noGray := (forall i: int :: memAddr(i) ==> !Gray(Color[i])); +} + +procedure {:yields} {:layer 98} {:refines "AtomicNoGrayInRootScanBarrier"} NoGrayInRootScanBarrier({:linear "tid"} tid:Tid) returns (noGray: bool) +requires {:layer 98} tid == GcTid && MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} collectorPhase == old(collectorPhase); +{ + yield; + assert {:layer 98} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); + call noGray := MsIsEmpty(tid); + assert {:layer 98} noGray || MST(0); + yield; + assert {:layer 98} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); +} + +procedure {:left} {:layer 99} AtomicInsertIntoSetIfWhiteInRootScanBarrier({:linear "tid"} tid:Tid, memLocal:int) +modifies Color; +{ + assert tid == GcTid && rootScanOn && mutatorsInRootScanBarrier == Mutators && memAddr(memLocal); + if (White(Color[memLocal])) { + Color[memLocal] := GRAY(); + } +} + +procedure {:yields} {:layer 98} {:refines "AtomicInsertIntoSetIfWhiteInRootScanBarrier"} InsertIntoSetIfWhiteInRootScanBarrier({:linear "tid"} tid:Tid, memLocal:int) +requires {:layer 98} tid == GcTid && MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} collectorPhase == old(collectorPhase); +{ + yield; + assert {:layer 98} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); + call MsPushByCollector(tid, memLocal); + assert {:layer 98} MST(MarkStackPtr-1); + yield; + assert {:layer 98} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); +} + +procedure {:left} {:layer 99,100} AtomicSET_InsertIntoSetIfWhite({:linear "tid"} tid:Tid, parent: int, child:int) +modifies Color; +{ + assert tid == GcTid; + assert MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase) && sweepPtr == memLo && memAddr(child); + if (White(Color[child])) { + Color[child] := GRAY(); + } +} + +procedure {:yields} {:layer 98} {:refines "AtomicSET_InsertIntoSetIfWhite"} SET_InsertIntoSetIfWhite({:linear "tid"} tid:Tid, parent: int, child:int) +requires {:layer 98} tid == GcTid && memAddr(parent) && memAddr(child) && MsWellFormed(MarkStack, MarkStackPtr, Color, parent); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, parent); +ensures {:layer 98} collectorPhase == old(collectorPhase); +{ + yield; + assert {:layer 98} tid == GcTid && memAddr(parent) && memAddr(child); + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, parent); + assert {:layer 98} collectorPhase == old(collectorPhase); + call MsPushByCollector(tid, child); + assert {:layer 98} MST(MarkStackPtr-1); + yield; + assert {:layer 98} tid == GcTid && memAddr(parent) && memAddr(child); + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, parent); + assert {:layer 98} collectorPhase == old(collectorPhase); +} + +procedure {:right} {:layer 99,100} AtomicSET_Peek({:linear "tid"} tid:Tid) returns (isEmpty: bool, val:int) +{ + assert tid == GcTid; + assert MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); + if (*) { + assume (memAddr(val) && !Unalloc(Color[val])); + isEmpty := false; + } else { + isEmpty := true; + } +} + +procedure {:yields} {:layer 98} {:refines "AtomicSET_Peek"} SET_Peek({:linear "tid"} tid:Tid) returns (isEmpty: bool, val:int) +requires {:layer 98} tid == GcTid && MsWellFormed(MarkStack, MarkStackPtr, Color, 0); +ensures {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, if isEmpty then 0 else val); +ensures {:layer 98} collectorPhase == old(collectorPhase); +{ + yield; + assert {:layer 98} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, 0); + assert {:layer 98} collectorPhase == old(collectorPhase); + assert {:layer 98} MST(MarkStackPtr - 1); + call isEmpty, val := MsPop(tid); + yield; + assert {:layer 98} tid == GcTid; + assert {:layer 98} MsWellFormed(MarkStack, MarkStackPtr, Color, if isEmpty then 0 else val); + assert {:layer 98} collectorPhase == old(collectorPhase); +} + +////////////////////////////////////////////////////////////////////////////// +// Phase 96 +////////////////////////////////////////////////////////////////////////////// + +procedure {:atomic} {:layer 97,100} AtomicSET_RemoveFromSet({:linear "tid"} tid:Tid, scannedLocal:int) +modifies Color; +{ + assert MarkPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); + assert tid == GcTid; + assert memAddr(scannedLocal); + Color[scannedLocal] := BLACK(); +} + +procedure {:yields} {:layer 96} {:refines "AtomicSET_RemoveFromSet"} SET_RemoveFromSet({:linear "tid"} tid:Tid, scannedLocal:int) +{ + yield; + call LockAcquire(tid); + call SetColor2(tid, scannedLocal, BLACK()); + call LockRelease(tid); + yield; +} + +procedure {:atomic} {:layer 97,98} AtomicMsPushByCollector({:linear "tid"} tid: Tid, val: int) +modifies Color, MarkStack, MarkStackPtr; +{ + assert memAddr(val) && tid == GcTid; + if (White(Color[val])) { + Color[val] := GRAY(); + MarkStack[MarkStackPtr] := val; + MarkStackPtr := MarkStackPtr + 1; + } +} + +procedure {:yields} {:layer 96} {:refines "AtomicMsPushByCollector"} MsPushByCollector({:linear "tid"} tid: Tid, val: int) +{ + var color:int; + var stack:int; + yield; + call LockAcquire(tid); + call color := ReadColorByCollector(tid, val); + if (White(color)) + { + call SetColor2(tid, val, GRAY()); + call stack := ReadMarkStackPtr(tid); + call WriteMarkStack(tid, stack, val); + stack := stack + 1; + call SetMarkStackPtr(tid, stack); + } + call LockRelease(tid); + yield; +} + +procedure {:atomic} {:layer 97,98} AtomicMsPushByMutator({:linear "tid"} tid: Tid, val: int) +modifies Color, MarkStack, MarkStackPtr; +{ + assert memAddr(val) && mutatorTidWhole(tid) && MarkPhase(mutatorPhase[i#Tid(tid)]); + if (White(Color[val])) { + Color[val] := GRAY(); + MarkStack[MarkStackPtr] := val; + MarkStackPtr := MarkStackPtr + 1; + } +} + +procedure {:yields} {:layer 96} {:refines "AtomicMsPushByMutator"} MsPushByMutator({:linear "tid"} tid: Tid, val: int) +{ + var color:int; + var stack:int; + yield; + call LockAcquire(tid); + call color := ReadColorByMutator2(tid, val); + if (White(color)) + { + call SetColor2(tid, val, GRAY()); + call stack := ReadMarkStackPtr(tid); + call WriteMarkStack(tid, stack, val); + stack := stack + 1; + call SetMarkStackPtr(tid, stack); + } + call LockRelease(tid); + yield; +} + +procedure{:atomic} {:layer 97,98} AtomicMsPop({:linear "tid"} tid:Tid) returns (isEmpty: bool, val:int) +modifies MarkStackPtr; +{ + assert tid == GcTid; + if (MarkStackPtr > 0) { + MarkStackPtr := MarkStackPtr - 1; + val := MarkStack[MarkStackPtr]; + isEmpty := false; + } else { + val := 0; + isEmpty := true; + } +} + +procedure{:yields} {:layer 96} {:refines "AtomicMsPop"} MsPop({:linear "tid"} tid:Tid) returns (isEmpty: bool, val:int) +{ + var stack:int; + yield; + call LockAcquire(tid); + call stack := ReadMarkStackPtr(tid); + if (stack > 0) + { + stack := stack - 1; + call SetMarkStackPtr(tid, stack); + call val := ReadMarkStack(tid, stack); + isEmpty := false; + } + else + { + val := 0; + isEmpty := true; + } + call LockRelease(tid); + yield; +} + +procedure{:atomic} {:layer 97,98} AtomicMsIsEmpty({:linear "tid"} tid: Tid) returns (isEmpty: bool) +{ assert tid == GcTid; isEmpty := MarkStackPtr == 0; } + +procedure{:yields} {:layer 96} {:refines "AtomicMsIsEmpty"} MsIsEmpty({:linear "tid"} tid: Tid) returns (isEmpty: bool) +{ + var v:int; + yield; + call LockAcquire(tid); + call v := ReadMarkStackPtr(tid); + isEmpty := v == 0; + call LockRelease(tid); + yield; +} + +procedure {:atomic} {:layer 97,100} AtomicResetSweepPtr({:linear "tid"} tid:Tid) +modifies sweepPtr; +{ assert tid == GcTid; sweepPtr := memLo; } + +procedure {:yields} {:layer 96} {:refines "AtomicResetSweepPtr"} ResetSweepPtr({:linear "tid"} tid:Tid) +{ + yield; + call LockAcquire(tid); + call SetSweepPtrLocked(tid, memLo); + call LockRelease(tid); + yield; +} + +procedure {:left} {:layer 97,100} AtomicSweepNext({:linear "tid"} tid:Tid) +modifies Color, sweepPtr; +{ + assert SweepPhase(collectorPhase) && PhaseConsistent(collectorPhase, mutatorPhase); + assert !Gray(Color[sweepPtr]); + assert tid == GcTid; + assert memAddr(sweepPtr); + Color[sweepPtr] := if White(Color[sweepPtr]) then UNALLOC() else if Black(Color[sweepPtr]) then WHITE() else Color[sweepPtr]; + sweepPtr := sweepPtr + 1; +} + +procedure {:yields} {:layer 96} {:refines "AtomicSweepNext"} SweepNext({:linear "tid"} tid:Tid) +{ + var color:int; + var sweep:int; + yield; + call LockAcquire(tid); + call sweep := ReadSweepPtr(tid); + call color := ReadColorByCollector(tid, sweep); + color := if White(color) then UNALLOC() else if Black(color) then WHITE() else color; + call SetColor(tid, sweep, color); + sweep := sweep + 1; + call SetSweepPtrLocked(tid, sweep); + call LockRelease(tid); + yield; +} + +procedure{:atomic} {:layer 97,100} AtomicHandshakeCollector({:linear "tid"} tid:Tid) returns (nextPhase: int) +modifies collectorPhase; +{ + assert tid == GcTid; + if (IdlePhase(collectorPhase)) { + collectorPhase := MARK(); + nextPhase := MARK(); + } else if (MarkPhase(collectorPhase)) { + collectorPhase := SWEEP(); + nextPhase := SWEEP(); + } else { + //assume (SweepPhase(collectorPhase)); + collectorPhase := IDLE(); + nextPhase := IDLE(); + } +} + +procedure{:yields} {:layer 96} {:refines "AtomicHandshakeCollector"} HandshakeCollector({:linear "tid"} tid:Tid) returns (nextPhase: int) +{ + var phase:int; + yield; + call LockAcquire(tid); + call phase := ReadCollectorPhase(tid); + nextPhase := if IdlePhase(phase) then MARK() else if MarkPhase(phase) then SWEEP() else IDLE(); + call SetCollectorPhase(tid, nextPhase); + call LockRelease(tid); + yield; +} + +procedure {:atomic} {:layer 97,100} AtomicUpdateMutatorPhase({:linear "tid"} tid: Tid) +modifies mutatorPhase; +{ assert mutatorTidWhole(tid); mutatorPhase[i#Tid(tid)] := collectorPhase; } + +procedure {:yields} {:layer 96} {:refines "AtomicUpdateMutatorPhase"} UpdateMutatorPhase({:linear "tid"} tid: Tid) +{ + var p:int; + yield; + call LockAcquire(tid); + call p := ReadCollectorPhaseLocked(tid); + call SetMutatorPhaseLocked(tid, p); + call LockRelease(tid); + yield; +} + +procedure {:atomic} {:layer 97,99} AtomicCollectorRootScanBarrierStart({:linear "tid"} tid: Tid) +modifies rootScanOn; +{ assert tid == GcTid; rootScanOn := true; } + +procedure {:yields} {:layer 96} {:refines "AtomicCollectorRootScanBarrierStart"} CollectorRootScanBarrierStart({:linear "tid"} tid: Tid) +{ + yield; + call LockAcquire(tid); + call CollectorRootScanBarrierStartLocked(tid); + call LockRelease(tid); + yield; +} + +procedure {:left} {:layer 97,99} AtomicCollectorRootScanBarrierEnd({:linear "tid"} tid: Tid) +modifies rootScanOn; +{ assert tid == GcTid; rootScanOn := false; } + +procedure {:yields} {:layer 96} {:refines "AtomicCollectorRootScanBarrierEnd"} CollectorRootScanBarrierEnd({:linear "tid"} tid: Tid) +{ + yield; + call LockAcquire(tid); + call CollectorRootScanBarrierEndLocked(tid); + call LockRelease(tid); + yield; +} + +procedure {:atomic} {:layer 97,99} AtomicCollectorRootScanBarrierWait({:linear "tid"} tid: Tid) +{ assert tid == GcTid; assume rootScanBarrier == 0; } + +procedure {:yields} {:layer 96} {:refines "AtomicCollectorRootScanBarrierWait"} CollectorRootScanBarrierWait({:linear "tid"} tid: Tid) +{ + var v:int; + + yield; + while (true) + { + yield; + call v := CollectorRootScanBarrierRead(tid); + yield; + if (v == 0) + { + yield; + return; + } + } + yield; +} + +procedure {:atomic} {:layer 97,99} AtomicMutatorRootScanBarrierEnter({:linear_in "tid"} tid: Tid) returns({:linear "tid"} tid_left: Tid) +modifies rootScanBarrier, mutatorsInRootScanBarrier; +{ + assert mutatorTidWhole(tid); + rootScanBarrier := rootScanBarrier - 1; + mutatorsInRootScanBarrier[i#Tid(tid)] := true; + tid_left := Tid(i#Tid(tid), true, false); +} + +procedure {:yields} {:layer 96} {:refines "AtomicMutatorRootScanBarrierEnter"} MutatorRootScanBarrierEnter({:linear_in "tid"} tid: Tid) returns({:linear "tid"} tid_left: Tid) +requires {:layer 95} mutatorTidWhole(tid); +ensures {:layer 95,96} i#Tid(tid_left) == i#Tid(tid) && left#Tid(tid_left); +{ + var{:linear "tid"} tid_right: Tid; + + yield; + call tid_left, tid_right := TidSplit(tid); + call LockAcquire(tid_left); + call MutatorsInRootScanBarrierAdd(tid_left, tid_right); + call AddRootScanBarrier(tid_left, -1); + call LockRelease(tid_left); + yield; +} + +procedure {:atomic} {:layer 97,99} AtomicMutatorRootScanBarrierWait({:linear_in "tid"} tid_left: Tid) returns({:linear "tid"} tid: Tid) +modifies rootScanBarrier, mutatorsInRootScanBarrier; +{ + assert mutatorTidLeft(tid_left) && mutatorsInRootScanBarrier[i#Tid(tid_left)]; + assume !rootScanOn; + rootScanBarrier := rootScanBarrier + 1; + mutatorsInRootScanBarrier[i#Tid(tid_left)] := false; + tid := Tid(i#Tid(tid_left), true, true); +} + +procedure {:yields} {:layer 96} {:refines "AtomicMutatorRootScanBarrierWait"} MutatorRootScanBarrierWait({:linear_in "tid"} tid_left: Tid) returns({:linear "tid"} tid: Tid) +ensures {:layer 95,96} i#Tid(tid) == i#Tid(tid_left) && left#Tid(tid) && right#Tid(tid); +{ + var{:linear "tid"} tid_right: Tid; + var b:bool; + + yield; + loop: + yield; + call LockAcquire(tid_left); + call b := MutatorReadBarrierOn(tid_left); + if (!b) + { + call AddRootScanBarrier(tid_left, 1); + call tid_right := MutatorsInRootScanBarrierRemove(tid_left); + call LockRelease(tid_left); + call tid := TidCombine(tid_left, tid_right); + yield; + return; + } + call LockRelease(tid_left); + yield; + goto loop; +} + +procedure {:atomic} {:layer 97,98} AtomicAllocIfPtrFree({:linear "tid"} tid:Tid, ptr:int, absPtr:obj) returns (spaceFound:bool) +modifies Color, toAbs, mem; +{ + assert mutatorTidWhole(tid) && memAddr(ptr) && (Unalloc(Color[ptr]) ==> toAbs[ptr] == nil); + if (*) { + assume Unalloc(Color[ptr]); + Color[ptr] := if sweepPtr <= ptr then BLACK() else WHITE(); + toAbs[ptr] := absPtr; + mem[ptr] := (lambda z: int :: if (fieldAddr(z)) then ptr else mem[ptr][z]); + spaceFound := true; + } else { + spaceFound := false; + } +} + +procedure {:yields} {:layer 96} {:refines "AtomicAllocIfPtrFree"} AllocIfPtrFree({:linear "tid"} tid:Tid, ptr:int, absPtr:obj) returns (spaceFound:bool) +{ + var color:int; + var sweep:int; + var t:[int]obj; + var fldIter:fld; + var {:layer 96} snapMem: [int][fld]int; + + yield; + call color := ReadColorByMutator1(tid, ptr); + if (Unalloc(color)) + { + yield; + call LockAcquire(tid); + call color := ReadColorByMutator2(tid, ptr); + if (Unalloc(color)) + { + spaceFound := true; + call sweep := ReadSweepPtr(tid); + if (sweep <= ptr) + { + color := BLACK(); + } + else + { + color := WHITE(); + } + + call snapMem := GhostReadMem(); + fldIter := 0; + while (fldIter < numFields) + invariant {:layer 96} 0 <= fldIter && fldIter <= numFields; + invariant {:layer 96} mem == snapMem[ptr := (lambda z: int :: if (0 <= z && z < fldIter) then ptr else snapMem[ptr][z])]; + { + call InitializeFieldInAlloc(tid, ptr, fldIter); + fldIter := fldIter + 1; + } + + call SetColor3(tid, ptr, color, absPtr); + call LockRelease(tid); + yield; + return; + } + call LockRelease(tid); + } + spaceFound := false; + yield; +} + +procedure {:atomic} {:layer 97,100} AtomicIsWhiteByCollector({:linear "tid"} tid:Tid, i: int) returns (isWhite: bool) +{ assert tid == GcTid && memAddr(i); isWhite := White(Color[i]); } + +procedure {:yields} {:layer 96} {:refines "AtomicIsWhiteByCollector"} IsWhiteByCollector({:linear "tid"} tid:Tid, i: int) returns (isWhite: bool) +{ + var v:int; + yield; + call LockAcquire(tid); + call v := ReadColorByCollector(tid, i); + isWhite := White(v); + call LockRelease(tid); + yield; +} + +procedure {:atomic} {:layer 97,100} AtomicClearToAbsWhite({:linear "tid"} tid:Tid) +modifies toAbs; +{ assert tid == GcTid; toAbs := (lambda x: int :: if memAddr(x) && White(Color[x]) then nil else toAbs[x]); } + +procedure {:yields} {:layer 96} {:refines "AtomicClearToAbsWhite"} ClearToAbsWhite({:linear "tid"} tid:Tid) +{ + yield; + call LockAcquire(tid); + call LockedClearToAbsWhite(tid); + call LockRelease(tid); + yield; +} + +////////////////////////////////////////////////////////////////////////////// +// Phase 95 +////////////////////////////////////////////////////////////////////////////// + +procedure {:atomic} {:layer 96} AtomicLockedClearToAbsWhite({:linear "tid"} tid:Tid) +modifies toAbs; +{ assert tid == GcTid && tidHasLock(tid, lock); toAbs := (lambda x: int :: if memAddr(x) && White(Color[x]) then nil else toAbs[x]); } + +procedure {:yields} {:layer 95} {:refines "AtomicLockedClearToAbsWhite"} LockedClearToAbsWhite({:linear "tid"} tid:Tid) +{ + yield; + call SetToAbs1(); + yield; +} + +procedure {:both} {:layer 96,99} AtomicInitField({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool, x: int, f: int) +modifies mem; +{ assert gcAndMutatorTids(tid, mutatorTids) && memAddr(x) && fieldAddr(f); mem[x][f] := x; } + +procedure {:yields} {:layer 95} {:refines "AtomicInitField"} InitField({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool, x: int, f: int) +{ + yield; + call PrimitiveWriteField(x, f, x); + yield; +} + +procedure {:atomic} {:layer 96,100} AtomicReadFieldCollector({:linear "tid"} tid:Tid, x:int, f: fld) returns (y: int) +{ assert tid == GcTid && memAddr(x) && fieldAddr(f) && toAbs[x] != nil; y := mem[x][f]; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadFieldCollector"} ReadFieldCollector({:linear "tid"} tid:Tid, x:int, f: fld) returns (y: int) +{ + yield; + call y := PrimitiveReadField(x, f); + yield; +} + +procedure {:atomic} {:layer 96,99} AtomicReadFieldGeneral({:linear "tid"} tid:Tid, x: int, f: fld) returns (y: int) +{ assert mutatorTidWhole(tid) && memAddr(x) && fieldAddr(f) && toAbs[x] != nil; y := mem[x][f]; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadFieldGeneral"} ReadFieldGeneral({:linear "tid"} tid:Tid, x: int, f: fld) returns (y: int) +{ + yield; + call y := PrimitiveReadField(x, f); + yield; +} + +procedure {:atomic} {:layer 96,99} AtomicWriteFieldGeneral({:linear "tid"} tid:Tid, x: int, f: fld, y: int) +modifies mem; +{ assert mutatorTidWhole(tid) && memAddr(x) && fieldAddr(f) && toAbs[x] != nil; mem[x][f] := y; } + +procedure {:yields} {:layer 95} {:refines "AtomicWriteFieldGeneral"} WriteFieldGeneral({:linear "tid"} tid:Tid, x: int, f: fld, y: int) +{ + yield; + call PrimitiveWriteField(x, f, y); + yield; +} + +procedure {:right} {:layer 96} AtomicInitializeFieldInAlloc({:linear "tid"} tid: Tid, ptr: int, fld: int) +modifies mem; +{ assert mutatorTidWhole(tid) && tidHasLock(tid, lock) && memAddr(ptr) && fieldAddr(fld) && toAbs[ptr] == nil; mem[ptr][fld] := ptr; } + +procedure {:yields} {:layer 95} {:refines "AtomicInitializeFieldInAlloc"} InitializeFieldInAlloc({:linear "tid"} tid: Tid, ptr: int, fld: int) +{ + yield; + call PrimitiveWriteField(ptr, fld, ptr); + yield; +} + +procedure{:both} {:layer 96} AtomicReadMarkStackPtr({:linear "tid"} tid:Tid) returns (val: int) +{ assert tidHasLock(tid, lock); val := MarkStackPtr; } + +procedure{:yields} {:layer 95} {:refines "AtomicReadMarkStackPtr"} ReadMarkStackPtr({:linear "tid"} tid:Tid) returns (val: int) +{ + yield; + call val := PrimitiveReadMarkStackPtr(); + yield; +} + +procedure{:atomic} {:layer 96,98} AtomicInitMarkStackPtr({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +modifies MarkStackPtr; +{ assert gcAndMutatorTids(tid, mutatorTids); MarkStackPtr := 0; } + +procedure{:yields} {:layer 95} {:refines "AtomicInitMarkStackPtr"} InitMarkStackPtr({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +{ + yield; + call PrimitiveSetMarkStackPtr(0); + yield; +} + +procedure{:both} {:layer 96} AtomicSetMarkStackPtr({:linear "tid"} tid:Tid, val: int) +modifies MarkStackPtr; +{ assert tidHasLock(tid, lock); MarkStackPtr := val; } + +procedure{:yields} {:layer 95} {:refines "AtomicSetMarkStackPtr"} SetMarkStackPtr({:linear "tid"} tid:Tid, val: int) +{ + yield; + call PrimitiveSetMarkStackPtr(val); + yield; +} + +procedure{:both} {:layer 96} AtomicReadMarkStack({:linear "tid"} tid:Tid, ptr: int) returns(val: int) +{ assert tidHasLock(tid, lock); val := MarkStack[ptr]; } + +procedure{:yields} {:layer 95} {:refines "AtomicReadMarkStack"} ReadMarkStack({:linear "tid"} tid:Tid, ptr: int) returns(val: int) +{ + yield; + call val := PrimitiveReadMarkStack(ptr); + yield; +} + +procedure{:both} {:layer 96} AtomicWriteMarkStack({:linear "tid"} tid:Tid, ptr: int, val: int) +modifies MarkStack; +{ assert tidHasLock(tid, lock); MarkStack[ptr] := val; } + +procedure{:yields} {:layer 95} {:refines "AtomicWriteMarkStack"} WriteMarkStack({:linear "tid"} tid:Tid, ptr: int, val: int) +{ + yield; + call PrimitiveWriteMarkStack(ptr, val); + yield; +} + +procedure {:both} {:layer 96,99} AtomicInitCollectorPhase({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +modifies collectorPhase; +{ assert gcAndMutatorTids(tid, mutatorTids); collectorPhase := IDLE(); } + +procedure {:yields} {:layer 95} {:refines "AtomicInitCollectorPhase"} InitCollectorPhase({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +{ + yield; + call PrimitiveSetCollectorPhase(IDLE()); + yield; +} + +procedure {:atomic} {:layer 96} AtomicReadCollectorPhase({:linear "tid"} tid: Tid) returns (phase:int) +{ assert tid == GcTid; phase := collectorPhase; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadCollectorPhase"} ReadCollectorPhase({:linear "tid"} tid: Tid) returns (phase:int) +{ + yield; + call phase := PrimitiveReadCollectorPhase(); + yield; +} + +procedure {:right} {:layer 96} AtomicReadCollectorPhaseLocked({:linear "tid"} tid: Tid) returns (phase:int) +{ assert mutatorTidWhole(tid) && tidHasLock(tid, lock); phase := collectorPhase; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadCollectorPhaseLocked"} ReadCollectorPhaseLocked({:linear "tid"} tid: Tid) returns (phase:int) +{ + yield; + call phase := PrimitiveReadCollectorPhase(); + yield; +} + +procedure {:both} {:layer 96} AtomicSetCollectorPhase({:linear "tid"} tid: Tid, phase:int) +modifies collectorPhase; +{ assert tid == GcTid && tidHasLock(tid, lock); collectorPhase := phase; } + +procedure {:yields} {:layer 95} {:refines "AtomicSetCollectorPhase"} SetCollectorPhase({:linear "tid"} tid: Tid, phase:int) +{ + yield; + call PrimitiveSetCollectorPhase(phase); + yield; +} + +procedure {:both} {:layer 96,99} AtomicInitMutatorPhase({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool, id: int) +modifies mutatorPhase; +{ assert gcAndMutatorTids(tid, mutatorTids); mutatorPhase[id] := IDLE(); } + +procedure {:yields} {:layer 95} {:refines "AtomicInitMutatorPhase"} InitMutatorPhase({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool, id: int) +{ + yield; + call PrimitiveSetMutatorPhase(id, IDLE()); + yield; +} + +procedure {:atomic} {:layer 96,100} AtomicReadMutatorPhaseByCollector({:linear "tid"} tid: Tid, i: int) returns (phase:int) +{ assert tid == GcTid; phase := mutatorPhase[i]; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadMutatorPhaseByCollector"} ReadMutatorPhaseByCollector({:linear "tid"} tid: Tid, i: int) returns (phase:int) +{ + yield; + call phase := PrimitiveReadMutatorPhase(i); + yield; +} + +procedure {:both} {:layer 96,99} AtomicReadMutatorPhase({:linear "tid"} tid: Tid) returns (phase:int) +{ assert mutatorTidWhole(tid); phase := mutatorPhase[i#Tid(tid)]; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadMutatorPhase"} ReadMutatorPhase({:linear "tid"} tid: Tid) returns (phase:int) +{ + yield; + call phase := PrimitiveReadMutatorPhase(i#Tid(tid)); + yield; +} + +procedure {:atomic} {:layer 96} AtomicSetMutatorPhaseLocked({:linear "tid"} tid: Tid, phase: int) +modifies mutatorPhase; +{ assert mutatorTidWhole(tid) && tidHasLock(tid, lock) && phase == collectorPhase; mutatorPhase[i#Tid(tid)] := phase; } + +procedure {:yields} {:layer 95} {:refines "AtomicSetMutatorPhaseLocked"} SetMutatorPhaseLocked({:linear "tid"} tid: Tid, phase: int) +{ + yield; + call PrimitiveSetMutatorPhase(i#Tid(tid), phase); + yield; +} + +procedure {:both} {:layer 96,99} AtomicInitSweepPtr({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +modifies sweepPtr; +{ assert gcAndMutatorTids(tid, mutatorTids); sweepPtr := memHi; } + +procedure {:yields} {:layer 95} {:refines "AtomicInitSweepPtr"} InitSweepPtr({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +{ + yield; + call PrimitiveSetSweepPtr(memHi); + yield; +} + +procedure {:both} {:layer 96} AtomicReadSweepPtr({:linear "tid"} tid:Tid) returns(val:int) +{ assert tidHasLock(tid, lock); val := sweepPtr; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadSweepPtr"} ReadSweepPtr({:linear "tid"} tid:Tid) returns(val:int) +{ + yield; + call val := PrimitiveReadSweepPtr(); + yield; +} + +procedure {:atomic} {:layer 96} AtomicSetSweepPtrLocked({:linear "tid"} tid:Tid, val: int) +modifies sweepPtr; +{ assert tid == GcTid && tidHasLock(tid, lock); sweepPtr := val; } + +procedure {:yields} {:layer 95} {:refines "AtomicSetSweepPtrLocked"} SetSweepPtrLocked({:linear "tid"} tid:Tid, val: int) +{ + yield; + call PrimitiveSetSweepPtr(val); + yield; +} + +procedure {:atomic} {:layer 96} AtomicCollectorRootScanBarrierStartLocked({:linear "tid"} tid: Tid) +modifies rootScanOn; +{ assert tid == GcTid && tidHasLock(tid, lock); rootScanOn := true; } + +procedure {:yields} {:layer 95} {:refines "AtomicCollectorRootScanBarrierStartLocked"} CollectorRootScanBarrierStartLocked({:linear "tid"} tid: Tid) +{ + yield; + call PrimitiveSetRootScanOn(true); + yield; +} + +procedure {:atomic} {:layer 96} AtomicCollectorRootScanBarrierEndLocked({:linear "tid"} tid: Tid) +modifies rootScanOn; +{ assert tid == GcTid && tidHasLock(tid, lock); rootScanOn := false; } + +procedure {:yields} {:layer 95} {:refines "AtomicCollectorRootScanBarrierEndLocked"} CollectorRootScanBarrierEndLocked({:linear "tid"} tid: Tid) +{ + yield; + call PrimitiveSetRootScanOn(false); + yield; +} + +procedure {:right} {:layer 96} AtomicMutatorReadBarrierOn({:linear "tid"} tid: Tid) returns (val:bool) +{ assert tidHasLock(tid, lock); val := rootScanOn; } + +procedure {:yields} {:layer 95} {:refines "AtomicMutatorReadBarrierOn"} MutatorReadBarrierOn({:linear "tid"} tid: Tid) returns (val:bool) +{ + yield; + call val := PrimitiveReadRootScanOn(); + yield; +} + +procedure {:yields} {:layer 95} PollMutatorReadBarrierOn({:linear "tid"} tid: Tid) returns (val:bool) +{ + yield; + call val := PrimitiveReadRootScanOn(); + yield; +} + +procedure{:atomic} {:layer 96,99} AtomicInitRootScanBarrier({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +modifies rootScanBarrier; +{ assert gcAndMutatorTids(tid, mutatorTids); rootScanBarrier := numMutators; } + +procedure{:yields} {:layer 95} {:refines "AtomicInitRootScanBarrier"} InitRootScanBarrier({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +{ + yield; + call PrimitiveSetRootScanBarrier(numMutators); + yield; +} + +procedure {:atomic} {:layer 96} AtomicCollectorRootScanBarrierRead({:linear "tid"} tid: Tid) returns (val:int) +{ assert tid == GcTid; val := rootScanBarrier; } + +procedure {:yields} {:layer 95} {:refines "AtomicCollectorRootScanBarrierRead"} CollectorRootScanBarrierRead({:linear "tid"} tid: Tid) returns (val:int) +{ + yield; + call val := PrimitiveReadRootScanBarrier(); + yield; +} + +procedure {:atomic} {:layer 96} AtomicAddRootScanBarrier({:linear "tid"} tid_left: Tid, val: int) +modifies rootScanBarrier; +{ assert mutatorTidLeft(tid_left) && tidHasLock(tid_left, lock); rootScanBarrier := rootScanBarrier + val; } + +procedure {:yields} {:layer 95} {:refines "AtomicAddRootScanBarrier"} AddRootScanBarrier({:linear "tid"} tid_left: Tid, val: int) +{ + yield; + call PrimitiveAddRootScanBarrier(val); + yield; +} + +procedure {:right} {:layer 96} AtomicMutatorsInRootScanBarrierAdd({:linear "tid"} tid_left: Tid, {:linear_in "tid"} tid_right: Tid) +modifies mutatorsInRootScanBarrier; +{ + assert tidHasLock(tid_left, lock) && mutatorTidRight(tid_right); + mutatorsInRootScanBarrier[i#Tid(tid_right)] := true; +} + +procedure {:yields} {:layer 95} {:refines "AtomicMutatorsInRootScanBarrierAdd"} MutatorsInRootScanBarrierAdd({:linear "tid"} tid_left: Tid, {:linear_in "tid"} tid_right: Tid) +{ + yield; + call PrimitiveMutatorsInRootScanBarrierAdd(tid_right); + yield; +} + +procedure {:both} {:layer 96} AtomicMutatorsInRootScanBarrierRemove({:linear "tid"} tid_left: Tid) returns({:linear "tid"} tid_right: Tid) +modifies mutatorsInRootScanBarrier; +{ + assert tidHasLock(tid_left, lock) && !rootScanOn && mutatorTidLeft(tid_left) && mutatorsInRootScanBarrier[i#Tid(tid_left)]; + mutatorsInRootScanBarrier[i#Tid(tid_left)] := false; + tid_right := Tid(i#Tid(tid_left), false, true); +} + +procedure {:yields} {:layer 95} {:refines "AtomicMutatorsInRootScanBarrierRemove"} MutatorsInRootScanBarrierRemove({:linear "tid"} tid_left: Tid) returns({:linear "tid"} tid_right: Tid) +ensures {:layer 95} i#Tid(tid_left) == i#Tid(tid_right); +ensures {:layer 95} left#Tid(tid_left) && right#Tid(tid_right); +{ + yield; + call tid_right := PrimitiveMutatorsInRootScanBarrierRemove(tid_left); + yield; +} + +procedure {:both} {:layer 96,99} AtomicInitRoot({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool, x: int) +modifies root; +{ assert gcAndMutatorTids(tid, mutatorTids) && rootAddr(x); root[x] := 0; } + +procedure {:yields} {:layer 95} {:refines "AtomicInitRoot"} InitRoot({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool, x: int) +{ + yield; + call PrimitiveWriteRoot(x, 0); + yield; +} + +procedure {:left} {:layer 96,99} AtomicReadRootInRootScanBarrier({:linear "tid"} tid:Tid, x: idx) returns (val: int) +{ assert tid == GcTid && rootAddr(x) && rootScanOn && mutatorsInRootScanBarrier == Mutators; val := root[x]; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadRootInRootScanBarrier"} ReadRootInRootScanBarrier({:linear "tid"} tid:Tid, x: idx) returns (val: int) +{ + yield; + call val := PrimitiveReadRoot(x); + yield; +} + +procedure {:both} {:layer 96,99} AtomicWriteRoot({:linear "tid"} tid: Tid, i: idx, val: int) +modifies root; +{ assert mutatorTidWhole(tid) && rootAddr(i) && tidOwns(tid, i); root[i] := val; } + +procedure {:yields} {:layer 95} {:refines "AtomicWriteRoot"} WriteRoot({:linear "tid"} tid: Tid, i: idx, val: int) +{ + yield; + call PrimitiveWriteRoot(i, val); + yield; +} + +procedure {:both} {:layer 96,99} AtomicReadRoot({:linear "tid"} tid: Tid, i: idx) returns (val: int) +{ assert mutatorTidWhole(tid) && rootAddr(i) && tidOwns(tid, i); val := root[i]; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadRoot"} ReadRoot({:linear "tid"} tid: Tid, i: idx) returns (val: int) +{ + yield; + call val := PrimitiveReadRoot(i); + yield; +} + +procedure {:both} {:layer 96,99} AtomicInitColor({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool, x: int) +modifies Color; +{ assert gcAndMutatorTids(tid, mutatorTids) && memAddr(x); Color[x] := UNALLOC(); } + +procedure {:yields} {:layer 95} {:refines "AtomicInitColor"} InitColor({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool, x: int) +{ + yield; + call PrimitiveSetColor(x, UNALLOC()); + yield; +} + +procedure {:both} {:layer 96} AtomicReadColorByCollector({:linear "tid"} tid:Tid, i: int) returns (val: int) +{ assert tid == GcTid && tidHasLock(tid, lock) && memAddr(i); val := Color[i]; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadColorByCollector"} ReadColorByCollector({:linear "tid"} tid:Tid, i: int) returns (val: int) +{ + yield; + call val := PrimitiveReadColor(i); + yield; +} + +procedure {:atomic} {:layer 96} AtomicReadColorByMutator1({:linear "tid"} tid:Tid, i: int) returns (val: int) +{ assert mutatorTidWhole(tid) && memAddr(i); } + +procedure {:yields} {:layer 95} {:refines "AtomicReadColorByMutator1"} ReadColorByMutator1({:linear "tid"} tid:Tid, i: int) returns (val: int) +{ + yield; + call val := PrimitiveReadColor(i); + yield; +} + +procedure {:both} {:layer 96} AtomicReadColorByMutator2({:linear "tid"} tid:Tid, i: int) returns (val: int) +{ assert mutatorTidWhole(tid) && tidHasLock(tid, lock) && memAddr(i); val := Color[i]; } + +procedure {:yields} {:layer 95} {:refines "AtomicReadColorByMutator2"} ReadColorByMutator2({:linear "tid"} tid:Tid, i: int) returns (val: int) +{ + yield; + call val := PrimitiveReadColor(i); + yield; +} + +procedure {:atomic} {:layer 96,98} AtomicReadColorByMutator3({:linear "tid"} tid:Tid, i: int) returns (val: int) +{ + assert mutatorTidWhole(tid) && memAddr(i) && MarkPhase(mutatorPhase[i#Tid(tid)]); + assume White(Color[i]) ==> White(val); +} + +procedure {:yields} {:layer 95} {:refines "AtomicReadColorByMutator3"} ReadColorByMutator3({:linear "tid"} tid:Tid, i: int) returns (val: int) +{ + yield; + call val := PrimitiveReadColor(i); + yield; +} + +procedure {:both} {:layer 96} AtomicSetColor({:linear "tid"} tid:Tid, i: int, val: int) +modifies Color; +{ assert tidHasLock(tid, lock) && memAddr(i) && PhaseConsistent(collectorPhase, mutatorPhase) && !MarkPhase(collectorPhase); Color[i] := val; } + +procedure {:yields} {:layer 95} {:refines "AtomicSetColor"} SetColor({:linear "tid"} tid:Tid, i: int, val: int) +{ + yield; + call PrimitiveSetColor(i, val); + yield; +} + +procedure {:left} {:layer 96} AtomicSetColor2({:linear "tid"} tid:Tid, i: int, val: int) +modifies Color; +{ + assert tidHasLock(tid, lock) && memAddr(i); + assert (MarkPhase(collectorPhase) || !PhaseConsistent(collectorPhase, mutatorPhase) ==> !White(val)); + Color[i] := val; +} + +procedure {:yields} {:layer 95} {:refines "AtomicSetColor2"} SetColor2({:linear "tid"} tid:Tid, i: int, val: int) +{ + yield; + call PrimitiveSetColor(i, val); + yield; +} + +procedure {:atomic} {:layer 96} AtomicSetColor3({:linear "tid"} tid:Tid, i: int, val: int, o: obj) +modifies Color, toAbs; +{ + assert tidHasLock(tid, lock) && memAddr(i); + assert White(val) ==> Unalloc(Color[i]); + Color[i] := val; + toAbs[i] := o; +} + +procedure {:yields} {:layer 95} {:refines "AtomicSetColor3"} SetColor3({:linear "tid"} tid:Tid, i: int, val: int, o: obj) +{ + yield; + call PrimitiveSetColor(i, val); + call SetToAbs2(i, o); + yield; +} + +procedure {:both} {:layer 96,99} AtomicInitToAbs({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +modifies toAbs; +{ + assert gcAndMutatorTids(tid, mutatorTids); + toAbs := (lambda i:int :: if memAddr(i) then nil else Int(i)); +} + +procedure {:yields} {:layer 95} {:refines "AtomicInitToAbs"} InitToAbs({:linear "tid"} tid:Tid, {:linear "tid"} mutatorTids:[int]bool) +{ + yield; + call SetToAbs3(); + yield; +} + +procedure {:right} {:layer 96} AtomicLockAcquire({:linear "tid"} tid: Tid) +modifies lock; +{ assert i#Tid(tid) != 0; assume lock == 0; lock := i#Tid(tid); } + +procedure {:yields} {:layer 95} {:refines "AtomicLockAcquire"} LockAcquire({:linear "tid"} tid: Tid) +{ + var status:bool; + yield; + while (true) + { + call status := PrimitiveLockCAS(i#Tid(tid)); + if (status) + { + yield; + return; + } + yield; + } + yield; +} + +procedure {:left} {:layer 96} AtomicLockRelease({:linear "tid"} tid:Tid) +modifies lock; +{ assert tidHasLock(tid, lock); lock := 0; } + +procedure {:yields} {:layer 95} {:refines "AtomicLockRelease"} LockRelease({:linear "tid"} tid:Tid) +{ + yield; + call PrimitiveLockZero(); + yield; +} + +procedure {:layer 96} {:inline 1} GhostReadMem() returns (snapMem: [int][fld]int) +{ + snapMem := mem; +} + +procedure {:layer 99} {:inline 1} GhostReadColor99() returns (snapColor: [int]int) +{ + snapColor := Color; +} + +procedure {:layer 100} {:inline 1} GhostReadColor100() returns (snapColor: [int]int) +{ + snapColor := Color; +} + +////////////////////////////////////////////////////////////////////////////// +// ATOMIC PRIMITIVES +// The action specifications, linearity specifications, and requires/ensures below here are trusted. +// (Note, though, that Boogie still verifies the mover types (atomic,left,right,both); these are not trusted.) +////////////////////////////////////////////////////////////////////////////// + +procedure {:both} {:layer 1,96} AtomicTidSplit({:linear_in "tid"} tid:Tid) returns({:linear "tid"} tid_left:Tid, {:linear "tid"} tid_right:Tid) +{ assert left#Tid(tid) && right#Tid(tid); tid_left := Tid(i#Tid(tid), true, false); tid_right := Tid(i#Tid(tid), false, true); } +procedure {:yields} {:layer 0} {:refines "AtomicTidSplit"} TidSplit({:linear_in "tid"} tid:Tid) returns({:linear "tid"} tid_left:Tid, {:linear "tid"} tid_right:Tid); + +procedure {:both} {:layer 1,96} AtomicTidCombine({:linear_in "tid"} tid_left:Tid, {:linear_in "tid"} tid_right:Tid) returns({:linear "tid"} tid:Tid) +{ assert i#Tid(tid_left) == i#Tid(tid_right) && left#Tid(tid_left) && right#Tid(tid_right); tid := Tid(i#Tid(tid_left), true, true); } +procedure {:yields} {:layer 0} {:refines "AtomicTidCombine"} TidCombine({:linear_in "tid"} tid_left:Tid, {:linear_in "tid"} tid_right:Tid) returns({:linear "tid"} tid:Tid); + +procedure {:both} {:layer 1,99} AtomicTidOutput({:linear_in "tid"} tid_in:Tid, {:linear_out "tid"} tid_out:Tid) +{ assert tid_in == tid_out; } +procedure {:yields} {:layer 0} {:refines "AtomicTidOutput"} TidOutput({:linear_in "tid"} tid_in:Tid, {:linear_out "tid"} tid_out:Tid); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveReadField(x: int, f: fld) returns (y: int) +{ assert memAddr(x) && fieldAddr(f); y := mem[x][f]; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveReadField"} PrimitiveReadField(x: int, f: fld) returns (y: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveWriteField(x: int, f: fld, y: int) +modifies mem; +{ assert memAddr(x) && fieldAddr(f); mem[x][f] := y; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveWriteField"} PrimitiveWriteField(x: int, f: fld, y: int); + +procedure {:right} {:layer 1,99} AtomicPrimitiveFindFreePtrAbs() returns (o: obj) +modifies allocSet; +{ assume (memAddrAbs(o) && !allocSet[o] && o != nil); allocSet[o] := true; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveFindFreePtrAbs"} PrimitiveFindFreePtrAbs() returns (o: obj); + +procedure{:atomic} {:layer 1,95} AtomicPrimitiveReadMarkStackPtr() returns (val: int) +{ val := MarkStackPtr; } +procedure{:yields} {:layer 0} {:refines "AtomicPrimitiveReadMarkStackPtr"} PrimitiveReadMarkStackPtr() returns (val: int); + +procedure{:atomic} {:layer 1,95} AtomicPrimitiveSetMarkStackPtr(val: int) +modifies MarkStackPtr; +{ MarkStackPtr := val; } +procedure{:yields} {:layer 0} {:refines "AtomicPrimitiveSetMarkStackPtr"} PrimitiveSetMarkStackPtr(val: int); + +procedure{:atomic} {:layer 1,95} AtomicPrimitiveReadMarkStack(ptr: int) returns (val: int) +{ val := MarkStack[ptr]; } +procedure{:yields} {:layer 0} {:refines "AtomicPrimitiveReadMarkStack"} PrimitiveReadMarkStack(ptr: int) returns (val: int); + +procedure{:atomic} {:layer 1,95} AtomicPrimitiveWriteMarkStack(ptr: int, val: int) +modifies MarkStack; +{ MarkStack[ptr] := val; } +procedure{:yields} {:layer 0} {:refines "AtomicPrimitiveWriteMarkStack"} PrimitiveWriteMarkStack(ptr: int, val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveReadCollectorPhase() returns (phase: int) +{ phase := collectorPhase; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveReadCollectorPhase"} PrimitiveReadCollectorPhase() returns (phase: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveSetCollectorPhase(phase:int) +modifies collectorPhase; +{ collectorPhase := phase; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveSetCollectorPhase"} PrimitiveSetCollectorPhase(phase:int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveReadMutatorPhase(i: int) returns (phase: int) +{ phase := mutatorPhase[i]; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveReadMutatorPhase"} PrimitiveReadMutatorPhase(i: int) returns (phase: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveSetMutatorPhase(i: int, phase: int) +modifies mutatorPhase; +{ mutatorPhase[i] := phase; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveSetMutatorPhase"} PrimitiveSetMutatorPhase(i: int, phase: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveReadSweepPtr() returns(val: int) +{ val := sweepPtr; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveReadSweepPtr"} PrimitiveReadSweepPtr() returns(val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveSetSweepPtr(val: int) +modifies sweepPtr; +{ sweepPtr := val; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveSetSweepPtr"} PrimitiveSetSweepPtr(val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveReadRootScanOn() returns(val: bool) +{ val := rootScanOn; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveReadRootScanOn"} PrimitiveReadRootScanOn() returns(val: bool); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveSetRootScanOn(val: bool) +modifies rootScanOn; +{ rootScanOn := val; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveSetRootScanOn"} PrimitiveSetRootScanOn(val: bool); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveReadRootScanBarrier() returns(val: int) +{ val := rootScanBarrier; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveReadRootScanBarrier"} PrimitiveReadRootScanBarrier() returns(val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveSetRootScanBarrier(val: int) +modifies rootScanBarrier; +{ rootScanBarrier := val; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveSetRootScanBarrier"} PrimitiveSetRootScanBarrier(val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveAddRootScanBarrier(val: int) +modifies rootScanBarrier; +{ rootScanBarrier := rootScanBarrier + val; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveAddRootScanBarrier"} PrimitiveAddRootScanBarrier(val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveMutatorsInRootScanBarrierAdd({:linear_in "tid"} tid_right: Tid) +modifies mutatorsInRootScanBarrier; +{ assert mutatorTidRight(tid_right); mutatorsInRootScanBarrier[i#Tid(tid_right)] := true; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveMutatorsInRootScanBarrierAdd"} PrimitiveMutatorsInRootScanBarrierAdd({:linear_in "tid"} tid_right: Tid); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveMutatorsInRootScanBarrierRemove({:linear "tid"} tid_left: Tid) returns({:linear "tid"} tid_right: Tid) +modifies mutatorsInRootScanBarrier; +{ assert mutatorTidLeft(tid_left) && mutatorsInRootScanBarrier[i#Tid(tid_left)]; mutatorsInRootScanBarrier[i#Tid(tid_left)] := false; tid_right := Tid(i#Tid(tid_left), false, true); } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveMutatorsInRootScanBarrierRemove"} PrimitiveMutatorsInRootScanBarrierRemove({:linear "tid"} tid_left: Tid) returns({:linear "tid"} tid_right: Tid); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveWriteRoot(i: idx, val: int) +modifies root; +{ assert rootAddr(i); root[i] := val; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveWriteRoot"} PrimitiveWriteRoot(i: idx, val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveReadRoot(i: idx) returns (val: int) +{ assert rootAddr(i); val := root[i]; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveReadRoot"} PrimitiveReadRoot(i: idx) returns (val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveReadColor(i: int) returns (val: int) +{ assert memAddr(i); val := Color[i]; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveReadColor"} PrimitiveReadColor(i: int) returns (val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveSetColor(i: int, val: int) +modifies Color; +{ assert memAddr(i); Color[i] := val; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveSetColor"} PrimitiveSetColor(i: int, val: int); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveLockCAS(next: int) returns (status: bool) +modifies lock; +{ + assert next != 0; + if (*) { + assume lock == 0; lock := next; status := true; + } else { + status := false; + } +} +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveLockCAS"} PrimitiveLockCAS(next: int) returns (status: bool); + +procedure {:atomic} {:layer 1,95} AtomicPrimitiveLockZero() +modifies lock; +{ lock := 0; } +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveLockZero"} PrimitiveLockZero(); + +procedure {:layer 99} {:inline 1} SetMemAbs1(x: idx, f: fld, y: idx) +modifies memAbs; +{ + memAbs[rootAbs[x]][f] := rootAbs[y]; +} + +procedure {:layer 99} {:inline 1} SetRootAbs1(x: idx, f: fld, y: idx) +modifies rootAbs; +{ + rootAbs[y] := memAbs[rootAbs[x]][f]; +} + +procedure {:layer 99} {:inline 1} SetMemAbs2(absPtr: obj) +modifies memAbs; +{ + memAbs[absPtr] := (lambda z: int :: if (fieldAddr(z)) then absPtr else memAbs[absPtr][z]); +} + +procedure {:layer 99} {:inline 1} SetRootAbs2(y: idx, absPtr: obj) +modifies rootAbs; +{ + rootAbs[y] := absPtr; +} + +procedure {:layer 95} {:inline 1} SetToAbs1() +modifies toAbs; +{ + toAbs := (lambda x: int :: if memAddr(x) && White(Color[x]) then nil else toAbs[x]); +} + +procedure {:layer 95} {:inline 1} SetToAbs2(i: int, o: obj) +modifies toAbs; +{ + toAbs[i] := o; +} + +procedure {:layer 95} {:inline 1} SetToAbs3() +modifies toAbs; +{ + toAbs := (lambda i:int :: if memAddr(i) then nil else Int(i)); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/GC.bpl.expect boogie-2.4.1+dfsg/Test/civl/GC.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/GC.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/GC.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1304 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ghost.bpl boogie-2.4.1+dfsg/Test/civl/ghost.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ghost.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/ghost.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -1,19 +1,25 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -var {:layer 0} x: int; +var {:layer 0,2} x: int; -procedure {:yields} {:layer 0,1} Incr(); -ensures {:right} |{ A: x := x + 1; return true; }|; +procedure {:right} {:layer 1} AtomicIncr() +modifies x; +{ x := x + 1; } -procedure {:pure} ghost(y: int) returns (z: int) +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(); + +procedure ghost(y: int) returns (z: int) requires y == 1; ensures z == 2; { z := y + 1; } -procedure {:yields} {:layer 1,2} Incr2() -ensures {:right} |{ A: x := x + 2; return true; }|; +procedure {:right} {:layer 2} AtomicIncr2() +modifies x; +{ x := x + 2; } + +procedure {:yields} {:layer 1} {:refines "AtomicIncr2"} Incr2() { var {:layer 1} a: int; @@ -30,8 +36,7 @@ z := x; } -procedure {:yields} {:layer 1,2} Incr2_0() -ensures {:right} |{ A: x := x + 2; return true; }|; +procedure {:yields} {:layer 1} {:refines "AtomicIncr2"} Incr2_0() { var {:layer 1} a: int; var {:layer 1} b: int; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ghost.bpl.expect boogie-2.4.1+dfsg/Test/civl/ghost.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ghost.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/ghost.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 8 verified, 0 errors +Boogie program verifier finished with 6 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/inc-dec.bpl boogie-2.4.1+dfsg/Test/civl/inc-dec.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/inc-dec.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/inc-dec.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,80 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// ########################################################################### +// Global shared variables + +var {:layer 0,2} x : int; + +// ########################################################################### +// Main + +procedure {:yields} {:layer 1} main (N: int) +requires {:layer 1} N >= 0; +{ + yield; + async call inc_by_N(N); + async call dec_by_N(N); + yield; +} + +procedure {:yields} {:layer 1} {:left} inc_by_N (N: int) +modifies x; +requires {:layer 1} N >= 0; +ensures {:layer 1} x == old(x) + N; +{ + var i : int; + + call dummy(); + + i := 0; + while (i != N) + invariant {:layer 1} x == old(x) + i; + invariant {:layer 1} {:terminates} true; + { + i := i + 1; + async call inc(); + call dummy(); + } + + call dummy(); +} + +procedure {:yields} {:layer 1} {:left} dec_by_N (N: int) +modifies x; +requires {:layer 1} N >= 0; +ensures {:layer 1} x == old(x) - N; +{ + var i : int; + + call dummy(); + + i := 0; + while (i != N) + invariant {:layer 1} x == old(x) - i; + invariant {:layer 1} {:terminates} true; + { + i := i + 1; + async call dec(); + call dummy(); + } + + call dummy(); +} + +procedure {:yields} {:layer 0} dummy (); + +// ########################################################################### +// Low level atomic actions + +procedure {:both} {:layer 1} atomic_inc () +modifies x; +{ x := x + 1; } + +procedure {:both} {:layer 1} atomic_dec () +modifies x; +{ x := x - 1; } + +procedure {:yields} {:layer 0} {:refines "atomic_inc"} inc (); + +procedure {:yields} {:layer 0} {:refines "atomic_dec"} dec (); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/inc-dec.bpl.expect boogie-2.4.1+dfsg/Test/civl/inc-dec.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/inc-dec.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/inc-dec.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 8 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/intro.bpl boogie-2.4.1+dfsg/Test/civl/intro.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/intro.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/intro.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,66 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 1,2} y:int; +var {:layer 0,1} x:int; + +procedure {:atomic} {:layer 2,2} atomic_read_y () returns (v:int) +{ v := y; } + +procedure {:atomic} {:layer 2,2} atomic_write_y (y':int) +modifies y; +{ y := y'; } + +procedure {:yields} {:layer 1} {:refines "atomic_read_y"} read_y () returns ({:layer 0} v:int) +requires {:layer 1} x == y; +{ + yield; assert {:layer 1} x == y; + call v := read_x(); + yield; +} + +procedure {:yields} {:layer 1} {:refines "atomic_write_y"} write_y (y':int) +requires {:layer 1} x == y; +{ + yield; assert {:layer 1} x == y; + call write_x(y'); + call set_y_to_x(); + yield; +} + +procedure {:layer 1,1} set_y_to_x () +ensures x == y; +modifies y; +{ + y := x; +} + +procedure {:atomic} {:layer 1,1} atomic_read_x () returns (v:int) +{ v := x; } + +procedure {:atomic} {:layer 1,1} atomic_write_x (x':int) +modifies x; +{ x := x'; } + +procedure {:yields} {:layer 0} {:refines "atomic_read_x"} read_x () returns ({:layer 0} v:int) +{ + yield; + call v := intro_read_x(); + yield; +} + +procedure {:yields} {:layer 0} {:refines "atomic_write_x"} write_x (x':int) +{ + yield; + call intro_write_x(x'); + yield; +} + +procedure {:layer 0} intro_read_x () returns (v:int) +ensures x == v; +{ v := x; } + +procedure {:layer 0} {:inline 1} intro_write_x (x':int) +ensures x == x'; +modifies x; +{ x := x'; } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/intro.bpl.expect boogie-2.4.1+dfsg/Test/civl/intro.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/intro.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/intro.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 8 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lamport.bpl boogie-2.4.1+dfsg/Test/civl/lamport.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lamport.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lamport.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,128 @@ +// Example from the paper "Teaching Concurrency" by Leslie Lamport +// http://lamport.azurewebsites.net/pubs/teaching-concurrency.pdf +// +// There are N processes (numbered from 0 to N-1) and two arrays x and y. Each +// process i executes the following sequence of statements: +// +// x[i] := 1; +// y[i] := x[(i-1) mod N]; +// +// Both lines are assumed to be atomic. This algorithm has the property that +// once all processes have finished, at least one y[j] == 1. + +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// Number of processes in the algorithm. There needs to be at least one. +const N : int; +axiom N >= 1; + +var {:layer 0,1} x : [int]int; +var {:layer 0,1} y : [int]int; +// Records whether or not a process has finished +var {:layer 1} done : [int]bool; + +// ############################################################################# + +// Main procedures that spawns all processes +procedure {:yields} {:layer 1} Main() +requires {:layer 1} done == (lambda i: int :: false); +ensures {:layer 1} safety(done, y); +{ + var i: int; + assert {:layer 1} Trigger(0); + yield; + assert {:layer 1} ind_inv(done, y, x); + i := 0; + while (i < N) + invariant {:layer 1} ind_inv(done, y, x); + { + async call Proc(i); + i := i + 1; + yield; + assert {:layer 1} ind_inv(done, y, x); + } + yield; + assert {:layer 1} ind_inv(done, y, x); +} + +// Code of process i +procedure {:yields} {:layer 1} Proc(i: int) +requires {:layer 1} ind_inv(done, y, x); +ensures {:layer 1} ind_inv(done, y, x); +{ + yield; + assert {:layer 1} ind_inv(done, y, x); + call update_x(i); + yield; + assert {:layer 1} x[i] == 1; + assert {:layer 1} ind_inv(done, y, x); + call update_y(i); + call mark_done(i); + assert {:layer 1} Trigger((i-1) mod N); + yield; + assert {:layer 1} ind_inv(done, y, x); +} + +// Introduction procedure that gives meaning to the introduced variable done +procedure {:layer 1}{:inline 1} mark_done(i: int) +modifies done; +{ + done := done[i:=true]; +} + +// ############################################################################# + +// Low-level atomic actions + +procedure {:layer 1}{:atomic} atomic_update_x(i: int) +modifies x; +{ + x[i] := 1; +} + +procedure {:layer 1}{:atomic} atomic_update_y(i: int) +modifies y; +{ + y[i] := x[(i-1) mod N]; +} + +procedure {:layer 0}{:yields}{:refines "atomic_update_x"} update_x(i: int); +procedure {:layer 0}{:yields}{:refines "atomic_update_y"} update_y(i: int); + +// ############################################################################# + +// Process IDs range from 0 to N-1 +function in_range(i: int): bool +{ + 0 <= i && i < N +} + +// The core correctness property of the system. If all the processes +// have finished, there's at least one element of y equal to 1. +function safety(done: [int]bool, y: [int]int): bool +{ + (forall i : int :: {Trigger(i)}{in_range(i)} in_range(i) ==> done[i]) + ==> + (exists i : int :: in_range(i) && y[i] == 1) +} + +// Records that all completed processes have their x equal to 1. +// This is weaker than the corresponding inductive invariant +// conjunct in other tools for the same algorithm. +function x_inv(done: [int]bool, x: [int]int): bool +{ + (forall i : int :: in_range(i) && done[i] ==> x[i] == 1) +} + +// Inductive invariant. Given the discussion at the top of this file, +// this should probably be considered part of the global inductive +// invariant. I think the assert x[i] == 1 in Proc is also, in some sense, +// part of the global inductive invariant. +function ind_inv(done: [int]bool, y: [int]int, x: [int]int): bool +{ + safety(done, y) && x_inv(done, x) +} + +// Dummy function to supply hints for quantifier reasoning +function Trigger(i: int) : bool { true } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lamport.bpl.expect boogie-2.4.1+dfsg/Test/civl/lamport.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lamport.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lamport.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 4 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lamport_prophecy.bpl boogie-2.4.1+dfsg/Test/civl/lamport_prophecy.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lamport_prophecy.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lamport_prophecy.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,121 @@ +// This is a specification of a simple concurrent algorithm from the paper +// "Teaching Concurrency" by Leslie Lamport. In there are N processes and +// two arrays of length N, x and y. Each process i executes the following +// sequence of statements: +// +// x[i] := 1; +// y[i] := x[(i-1) mod N]; +// +// The reads and writes of each x[j] are assumed to be atomic. This +// algorithm has the property that once all processes have finished, at +// least one y[j] == 1. + +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +// XFAIL: * + +// Number of processes in the algorithm. There needs to be at least one. +const N : int; +axiom N > 0; + +function {:inline} IsProcId(i: int) : bool { 0 <= i && i < N } + +var {:layer 0, 1} x : [int]int; +var {:layer 0, 3} y : [int]int; + +// Prophecy variable and associated constant +var {:layer 1, 1} p : int; +const c: int; + +function {:inline} Invariant(i: int, p: int, c: int, x: [int]int) : bool { + IsProcId(i) && + IsProcId(p) && + IsProcId(c) && + (p == c || x[c] == 1) +} + +// ############################################################################# + +// Main procedures that spawns all processes +procedure {:layer 2}{:yields}{:refines "atomic_main_abs"} Main() +requires {:layer 1} IsProcId(p) && IsProcId(c) && p == c; +{ + var i: int; + i := 0; + yield; + assert {:layer 1} Invariant(i, p, c, x); + while (i < N) + invariant {:terminates} {:layer 0,1,2} true; + invariant {:layer 1} (IsProcId(i) || i == N) && IsProcId(p) && IsProcId(c) && (p == c || x[c] == 1); + invariant {:layer 2} (IsProcId(i) || i == N) && IsProcId(c) && (i <= (c+1) mod N || y[(c+1) mod N] == 1); + { + async call Proc(i); + i := i + 1; + } + yield; +} + +procedure {:layer 3}{:atomic} atomic_main_abs() +modifies y; +{ + var y': [int]int; + assert IsProcId(c); + assume y'[(c+1) mod N] == 1; + y := y'; +} + +// ############################################################################# + +// The specification of a process +procedure {:layer 1}{:yields}{:refines "atomic_update_y_abs"} Proc(i: int) +requires {:layer 1} Invariant(i, p, c, x); +{ + yield; + assert {:layer 1} Invariant(i, p, c, x); + call update_x(i); + call backward_assign_p(i); + yield; + assert {:layer 1} x[c] == 1; + call update_y(i); + yield; +} + +procedure {:layer 1}{:inline 1} backward_assign_p(i: int) +modifies p; +{ + assume p == i; + havoc p; + assume IsProcId(p); +} + +procedure {:layer 2}{:left} atomic_update_y_abs(i: int) +modifies y; +{ + if (i == (c+1) mod N) { + y[i] := 1; + } else { + havoc y; + assume y == old(y)[i := y[i]]; + } +} + +// ############################################################################# + +// Low-level atomic actions + +procedure {:layer 1}{:atomic} atomic_update_x(i: int) +modifies x; +{ + x[i] := 1; +} + +procedure {:layer 1}{:atomic} atomic_update_y(i: int) +modifies y; +{ + y[i] := x[(i-1) mod N]; +} + +procedure {:layer 0}{:yields}{:refines "atomic_update_x"} update_x(i: int); +procedure {:layer 0}{:yields}{:refines "atomic_update_y"} update_y(i: int); + +// ############################################################################# diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lamport_prophecy.bpl.expect boogie-2.4.1+dfsg/Test/civl/lamport_prophecy.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lamport_prophecy.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lamport_prophecy.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 7 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lease.bpl boogie-2.4.1+dfsg/Test/civl/lease.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lease.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lease.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,198 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// datatype lockMsg = transfer(epoch:int) | locked(epoch:int) +type{:datatype} lockMsg; +function{:constructor} transfer(epoch:int):lockMsg; +function{:constructor} locked(epoch:int):lockMsg; + +// datatype msg = msg(src:int, dst:int, payload:lockMsg) +type{:datatype} msg; +function{:constructor} msg(src:int, dst:int, payload:lockMsg):msg; + +// datatype node = node(held:bool, epoch:int) +type{:datatype} node; +function{:constructor} node(held:bool, epoch:int):node; + +// var network:set +var{:layer 1,2} network:[msg]bool; + +// var external:set +var{:layer 1,3} external:[msg]bool; + +// var nodes:imap +var{:layer 1,2} nodes:[int]node; + +// datatype history = history(len:int, locks:[int]int) +type{:datatype} history; +function{:constructor} history(len:int, locks:[int]int):history; + +var{:layer 1,3} history:history; + +function addHistory(h:history, l:int):history +{ + history(len#history(h) + 1, locks#history(h)[len#history(h) := l]) +} + +function nextNode(me:int):int; + + +////// primitive actions ////// + +procedure{:both}{:layer 2} AtomicGetNode({:linear "me"} me:int) returns(n:node) +{ + n := nodes[me]; +} + +procedure{:yields}{:layer 1} {:refines "AtomicGetNode"} GetNode({:linear "me"} me:int) returns(n:node); + +procedure{:both}{:layer 2} AtomicSetNode({:linear "me"} me:int, n:node) +modifies nodes; +{ + nodes := nodes[me := n]; +} + +procedure{:yields}{:layer 1} {:refines "AtomicSetNode"} SetNode({:linear "me"} me:int, n:node); + +procedure{:right}{:layer 2} AtomicRecv({:linear "me"} me:int) returns(m:msg) +{ + assume network[m] && dst#msg(m) == me; +} + +procedure{:yields}{:layer 1} {:refines "AtomicRecv"} Recv({:linear "me"} me:int) returns(m:msg); + +procedure{:left}{:layer 2} AtomicSendInternal({:linear "me"} me:int, dst:int, payload:lockMsg) +modifies network; +{ + network := network[msg(me, dst, payload) := true]; +} + +procedure{:yields}{:layer 1} {:refines "AtomicSendInternal"} SendInternal({:linear "me"} me:int, dst:int, payload:lockMsg); + +procedure{:left}{:layer 2} AtomicSendExternal({:linear "me"} me:int, dst:int, payload:lockMsg) +modifies network, external; +{ + network := network [msg(me, dst, payload) := true]; + external := external[msg(me, dst, payload) := true]; +} + +procedure{:yields}{:layer 1} {:refines "AtomicSendExternal"} SendExternal({:linear "me"} me:int, dst:int, payload:lockMsg); + +procedure{:atomic}{:layer 2} AtomicAddHistory(l:int) +modifies history; +{ + history := addHistory(history, l); +} + +procedure{:yields}{:layer 1} {:refines "AtomicAddHistory"} AddHistory(l:int); +////// composite actions ////// + +function EpochInHistory(epoch:int, history:history):bool +{ + 0 <= epoch && epoch < len#history(history) +} + +function{:inline} IsFreshTransfer(network:[msg]bool, nodes:[int]node, m:msg):bool +{ + network[m] && is#transfer(payload#msg(m)) && epoch#transfer(payload#msg(m)) > epoch#node(nodes[dst#msg(m)]) +} + +function InvMsg(network:[msg]bool, nodes:[int]node, history:history, m:msg):bool +{ + is#transfer(payload#msg(m)) ==> + EpochInHistory(epoch#transfer(payload#msg(m)) - 1, history) + && dst#msg(m) == locks#history(history)[epoch#transfer(payload#msg(m)) - 1] + && (IsFreshTransfer(network, nodes, m) ==> len#history(history) == epoch#transfer(payload#msg(m))) +} + +function InvNode(history:history, n:node):bool +{ + held#node(n) ==> len#history(history) == epoch#node(n) +} + +function Inv(network:[msg]bool, nodes:[int]node, history:history):bool +{ + 0 <= len#history(history) +&& (forall i:int :: InvNode(history, nodes[i])) +&& (forall i1:int, i2:int :: held#node(nodes[i1]) && held#node(nodes[i2]) ==> i1 == i2) +&& (forall i1:int, m2:msg :: held#node(nodes[i1]) && IsFreshTransfer(network, nodes, m2) ==> false) +&& (forall m1:msg, m2:msg :: IsFreshTransfer(network, nodes, m1) && IsFreshTransfer(network, nodes, m2) ==> m1 == m2) +&& (forall m:msg :: network[m] ==> InvMsg(network, nodes, history, m)) +} + +procedure{:atomic}{:layer 3} AtomicGrant({:linear "me"} me:int) returns(dst:int, epoch:int) +modifies history; +{ + history := addHistory(history, dst); +} + +procedure{:yields}{:layer 2} {:refines "AtomicGrant"} Grant({:linear "me"} me:int) returns(dst:int, epoch:int) + requires{:layer 2} held#node(nodes[me]); + requires{:layer 2} Inv(network, nodes, history); + ensures {:layer 2} Inv(network, nodes, history); +{ + var node:node; + yield; assert{:layer 2} Inv(network, nodes, history) && held#node(nodes[me]); + + call node := GetNode(me); + dst := nextNode(me); + epoch := epoch#node(node); + call AddHistory(dst); + call SetNode(me, node(false, epoch)); + call SendInternal(me, dst, transfer(epoch + 1)); + + yield; assert{:layer 2} Inv(network, nodes, history); +} + +procedure{:atomic}{:layer 3} AtomicAccept({:linear "me"} me:int, dst:int) returns(epoch:int) +modifies external; +{ + // specify that the message source (me) must appear at right epoch in history: + assume EpochInHistory(epoch - 1, history); + assume me == locks#history(history)[epoch - 1]; + + external := external[msg(me, dst, locked(epoch)) := true]; +} + +procedure{:yields}{:layer 2} {:refines "AtomicAccept"} Accept({:linear "me"} me:int, dst:int) returns(epoch:int) + requires{:layer 2} Inv(network, nodes, history); + ensures {:layer 2} Inv(network, nodes, history); +{ + var node:node; + var m:msg; + yield; assert{:layer 2} Inv(network, nodes, history); + + while (true) + invariant{:layer 2} Inv(network, nodes, history); + { + yield; assert{:layer 2} Inv(network, nodes, history); + + call m := Recv(me); + call node := GetNode(me); + epoch := epoch#transfer(payload#msg(m)); + + if (is#transfer(payload#msg(m)) && epoch > epoch#node(node)) + { + call SetNode(me, node(true, epoch)); + call SendExternal(me, dst, locked(epoch)); + + yield; assert{:layer 2} Inv(network, nodes, history); + return; + } + } + + yield; assert{:layer 2} Inv(network, nodes, history); +} + +procedure CheckInitInv(network:[msg]bool, nodes:[int]node, history:history) + requires network == (lambda m:msg :: false); + requires nodes == (lambda i:int :: node(i == 0, if i == 0 then 1 else 0)); + requires history == history(1, (lambda i:int :: 0)); + ensures Inv(network, nodes, history); +{ +} + +////////////////////////////////////////////////////////////////////////////////////////// + +function {:builtin "MapConst"} MapConstBool(bool):[int]bool; +function {:linear "me"} IntCollector(x:int):[int]bool { MapConstBool(false)[x := true] } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lease.bpl.expect boogie-2.4.1+dfsg/Test/civl/lease.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lease.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lease.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 21 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/left-mover.bpl boogie-2.4.1+dfsg/Test/civl/left-mover.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/left-mover.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/left-mover.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,27 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 0,2} x : int; + +procedure {:left} {:layer 1} inc () +modifies x; +{ x := x + 1; } + +// Error: Gate failure of ass_eq_1 not preserved by inc +procedure {:atomic} {:layer 1} ass_eq_1 () +{ assert x == 1; } + +// Correct +procedure {:atomic} {:layer 1} ass_leq_1 () +{ assert x <= 1; } + +// Error: init and inc do not commute +procedure {:atomic} {:layer 1} init () +modifies x; +{ x := 0; } + +//////////////////////////////////////////////////////////////////////////////// + +// Error block is blocking +procedure {:left} {:layer 2} block () +{ assume x >= 0; } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/left-mover.bpl.expect boogie-2.4.1+dfsg/Test/civl/left-mover.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/left-mover.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/left-mover.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,17 @@ +(0,0): Error BP5003: A postcondition might not hold on this return path. +left-mover.bpl(11,32): Related location: Gate failure of ass_eq_1_1 not preserved by inc_1 +Execution trace: + left-mover.bpl(6,30): inline$inc_1$0$Entry + left-mover.bpl(8,5): inline$inc_1$0$anon0 + left-mover.bpl(6,30): inline$inc_1$0$Return +(0,0): Error BP5003: A postcondition might not hold on this return path. +left-mover.bpl(19,32): Related location: Commutativity check between init_1 and inc_1 failed +Execution trace: + left-mover.bpl(19,32): inline$init_1$0$Entry + left-mover.bpl(8,5): inline$inc_1$0$anon0 + left-mover.bpl(6,30): inline$inc_1$0$Return +left-mover.bpl(26,30): Error: Non-blocking check for block_2 failed +Execution trace: + left-mover.bpl(26,30): L + +Boogie program verifier finished with 1 verified, 3 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/allocator.bpl boogie-2.4.1+dfsg/Test/civl/linear/allocator.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/allocator.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/allocator.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,18 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +function {:builtin "MapConst"} MapConstBool(bool) : [int]bool; +function {:inline} {:linear "tid"} TidCollector(x: int) : [int]bool +{ + MapConstBool(false)[x := true] +} + +procedure A({:linear_in "tid"} i': int) returns ({:linear "tid"} i: int); + ensures i == i'; + +procedure B({:linear_in "tid"} i': int) returns ({:linear "tid"} i: int) +{ + i := i'; + call i := A(i); + assert false; +} + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/allocator.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear/allocator.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/allocator.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/allocator.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,5 @@ +allocator.bpl(16,3): Error BP5001: This assertion might not hold. +Execution trace: + allocator.bpl(14,5): anon0 + +Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/async-bug.bpl boogie-2.4.1+dfsg/Test/civl/linear/async-bug.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/async-bug.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/async-bug.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,42 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +const GcTid:int; + +function {:builtin "MapConst"} MapConstBool(bool) : [int]bool; +function {:inline} {:linear "tid"} TidCollector(x: int) : [int]bool +{ + MapConstBool(false)[x := true] +} + +procedure {:yields} {:layer 100} Initialize({:linear "tid"} tid:int) +requires{:layer 100} tid == GcTid; +{ + yield; + assert{:layer 100} tid == GcTid; + + call GarbageCollect(tid); + + yield; + assert{:layer 100} tid == GcTid; + + async call GarbageCollect(tid); + + yield; + assert{:layer 100} tid == GcTid; + + async call GarbageCollect(tid); + + yield; + assert{:layer 100} tid == GcTid; + + yield; + assert{:layer 100} tid == GcTid; +} + +procedure {:yields} {:layer 100} GarbageCollect({:linear "tid"} tid:int) +requires{:layer 100} tid == GcTid; +{ + yield; + assert{:layer 100} tid == GcTid; +} + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/async-bug.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear/async-bug.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/async-bug.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/async-bug.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,3 @@ +async-bug.bpl(27,30): Error: unavailable source for a linear read +async-bug.bpl(34,0): Error: Input variable tid must be available at a return +2 type checking errors detected in async-bug.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/bug.bpl boogie-2.4.1+dfsg/Test/civl/linear/bug.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/bug.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/bug.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,16 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +function {:builtin "MapConst"} MapConstBool(bool) : [int]bool; +function {:inline} {:linear ""} LinearIntDistinctness(x:int) : [int]bool { MapConstBool(false)[x := true] } + +var {:linear ""} g:int; + +procedure A() +{ +} + +procedure B() +{ + call A(); + assert false; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/bug.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear/bug.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/bug.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/bug.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,5 @@ +bug.bpl(15,3): Error BP5001: This assertion might not hold. +Execution trace: + bug.bpl(14,3): anon0 + +Boogie program verifier finished with 1 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f1.bpl boogie-2.4.1+dfsg/Test/civl/linear/f1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f1.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/f1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,53 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +function {:builtin "MapConst"} mapconstbool(bool) : [int]bool; +const {:existential true} b0: bool; +const {:existential true} b1: bool; +const {:existential true} b2: bool; +const {:existential true} b3: bool; +const {:existential true} b4: bool; +const {:existential true} b5: bool; +const {:existential true} b6: bool; +const {:existential true} b7: bool; +const {:existential true} b8: bool; + +axiom(b0); +axiom(b1); +axiom(b2); +axiom(b3); +axiom(b4); +axiom(b5); +axiom(!b6); +axiom(!b7); +axiom(b8); + +function {:inline} {:linear "1"} SetCollector1(x: [int]bool) : [int]bool +{ + x +} + +procedure main({:linear_in "1"} x_in: [int]bool) + requires b0 ==> x_in == mapconstbool(true); + requires b1 ==> x_in != mapconstbool(false); +{ + var {:linear "1"} x: [int] bool; + x := x_in; + + call foo(x); + + assert b6 ==> x == mapconstbool(true); + assert b7 ==> x != mapconstbool(false); + assert b8 ==> x == mapconstbool(false); +} + +procedure foo({:linear_in "1"} x_in: [int]bool) + requires b2 ==> x_in == mapconstbool(true); + requires b3 ==> x_in != mapconstbool(false); +{ + var {:linear "1"} x: [int] bool; + x := x_in; + + assert b4 ==> x == mapconstbool(true); + assert b5 ==> x != mapconstbool(false); + +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f1.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear/f1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/f1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,5 @@ +f1.bpl(40,4): Error BP5001: This assertion might not hold. +Execution trace: + f1.bpl(34,6): anon0 + +Boogie program verifier finished with 1 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f2.bpl boogie-2.4.1+dfsg/Test/civl/linear/f2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f2.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/f2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,27 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +function {:builtin "MapConst"} mapconstbool(bool) : [int]bool; +function {:builtin "MapOr"} mapunion([int]bool, [int]bool) : [int]bool; + +function {:inline} {:linear "1"} SetCollector1(x: [int]bool) : [int]bool +{ + x +} + +procedure Split({:linear_in "1"} xls: [int]bool) returns ({:linear "1"} xls1: [int]bool, {:linear "1"} xls2: [int]bool); +ensures xls == mapunion(xls1, xls2) && xls1 != mapconstbool(false) && xls2 != mapconstbool(false); + +procedure Allocate() returns ({:linear "1"} x: [int]bool); + +procedure main() +{ + var {:linear "1"} x: [int] bool; + var {:linear "1"} x1: [int] bool; + var {:linear "1"} x2: [int] bool; + + call x := Allocate(); + assume x == mapconstbool(true); + + call x1, x2 := Split(x); + assert false; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f2.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear/f2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/f2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,5 @@ +f2.bpl(26,4): Error BP5001: This assertion might not hold. +Execution trace: + f2.bpl(22,4): anon0 + +Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f3.bpl boogie-2.4.1+dfsg/Test/civl/linear/f3.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f3.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/f3.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,16 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +function {:builtin "MapConst"} MapConstBool(bool) : [int]bool; +function {:inline} {:linear "tid"} TidCollector(x: int) : [int]bool +{ + MapConstBool(false)[x := true] +} + +procedure A() {} + +procedure B({:linear_in "tid"} tid:int) returns({:linear "tid"} tid':int) +{ + tid' := tid; + call A(); +} + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f3.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear/f3.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/f3.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/f3.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 2 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/list.bpl boogie-2.4.1+dfsg/Test/civl/linear/list.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/list.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/list.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,50 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +type X; +function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; +function {:builtin "MapOr"} MapOr([X]bool, [X]bool) : [X]bool; + +function {:inline} {:linear "Mem"} MemCollector(xs: [X]bool) : [X]bool +{ + xs +} + +var head: X; +var tail: X; +var {:linear "Mem"} D: [X]bool; +var Next:[X]X; +const nil: X; + +procedure malloc() returns (x: X, {:linear "Mem"} M: [X]bool); +ensures M == MapConstBool(false)[x := true]; + +procedure Join({:linear_in "Mem"} A: [X]bool); +modifies D; +ensures MapOr(old(D), A) == D; + +procedure one() +requires D[head] && D[tail]; +requires (forall d: X :: {D[d]} D[d] ==> D[Next[d]] || d == tail); +ensures D[head] && D[tail]; +ensures (forall d: X :: {D[d]} D[d] ==> D[Next[d]] || d == tail); +ensures head != tail; +{ + var x: X; + var {:linear "Mem"} M: [X]bool; + + call x, M := malloc(); + call Join(M); + Next[tail] := x; + tail := x; + Next[tail] := nil; +} + +procedure two() +requires head != tail; +requires D[head] && D[tail]; +requires (forall d: X :: {D[d]} D[d] ==> D[Next[d]] || d == tail); +ensures (forall d: X :: {D[d]} D[d] ==> D[Next[d]] || d == tail); +ensures D[head] && D[tail]; +{ + head := Next[head]; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/list.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear/list.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/list.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/list.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 2 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/typecheck.bpl boogie-2.4.1+dfsg/Test/civl/linear/typecheck.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/typecheck.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/typecheck.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,131 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +type X; + +function {:inline} none() : [int]bool { (lambda i:int :: false) } +function {:inline} {:linear "A"} A_X_Collector(a: X) : [int]bool { none() } +function {:inline} {:linear "A"} A_int_Collector(a: int) : [int]bool { none() } +function {:inline} {:linear "B"} B_X_Collector(a: X) : [int]bool { none() } +function {:inline} {:linear "B"} B_XSet_Collector(a: [X]bool) : [int]bool { none() } +function {:inline} {:linear "C"} C_X_Collector(a: X) : [int]bool { none() } +function {:inline} {:linear "C"} C_XMulSet_Collector(a: [X]int) : [int]bool { none() } +function {:inline} {:linear "D"} D_X_Collector(a: X) : [int]bool { none() } +function {:inline} {:linear "D"} D_XSet_Collector(a: [X]bool) : [int]bool { none() } +function {:inline} {:linear "D2"} A2_X_Collector(a: X) : [int]bool { none() } +function {:inline} {:linear "x"} x_int_Collector(a: int) : [int]bool { none() } +function {:inline} {:linear ""} empty_int_Collector(a: int) : [int]bool { none() } + +procedure A() +{ + var {:linear "A"} a: X; + var {:linear "A"} b: int; +} + +procedure B() +{ + var {:linear "B"} a: X; + var {:linear "B"} b: [X]bool; +} + +procedure C() +{ + var {:linear "C"} a: X; + var {:linear "C"} c: [X]int; +} + +function f(X): X; + +procedure {:yields} {:layer 1} D() +{ + var {:linear "D"} a: X; + var {:linear "D"} x: X; + var {:linear "D"} b: [X]bool; + var c: X; + var {:linear "D2"} d: X; + + b[a] := true; + + a := f(a); + + a := c; + + c := a; + + a := d; + + a := a; + + a, x := x, a; + + a, x := x, x; + + call a, x := E(a, x); + + call a, x := E(a, a); + + call a, x := E(a, f(a)); + + call a, x := E(a, d); + + call d, x := E(a, x); + + call a, x := E(c, x); + + call c, x := E(a, x); + + yield; + par a := F(a) | x := F(a); + yield; +} + +procedure {:yields} {:layer 1} E({:linear_in "D"} a: X, {:linear_in "D"} b: X) returns ({:linear "D"} c: X, {:linear "D"} d: X) +{ + yield; + c := a; + yield; +} + +procedure {:yields} {:layer 0} F({:linear_in "D"} a: X) returns ({:linear "D"} c: X); + +var {:linear "x"} g:int; + +procedure G(i:int) returns({:linear "x"} r:int) +{ + r := g; +} + +procedure H(i:int) returns({:linear "x"} r:int) +modifies g; +{ + g := r; +} + +procedure {:yields} {:layer 0} I({:linear_in ""} x:int) returns({:linear ""} x':int) +{ + yield; + x' := x; + yield; +} + +procedure {:yields} {:layer 0} J() +{ + yield; +} + +procedure {:yields} {:layer 1} P1({:linear_in ""} x:int) returns({:linear ""} x':int) +{ + yield; + par x' := I(x) | J(); + yield; + call x' := I(x'); + yield; +} + +procedure {:yields} {:layer 1} P2({:linear_in ""} x:int) returns({:linear ""} x':int) +{ + yield; + call x' := I(x); + yield; + par x' := I(x') | J(); + yield; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/typecheck.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear/typecheck.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear/typecheck.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear/typecheck.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,16 @@ +typecheck.bpl(46,9): Error: Only simple assignment allowed on linear variable b +typecheck.bpl(48,6): Error: Only variable can be assigned to linear variable a +typecheck.bpl(50,6): Error: Only linear variable can be assigned to linear variable a +typecheck.bpl(54,6): Error: Linear variable of domain D2 cannot be assigned to linear variable of domain D +typecheck.bpl(60,9): Error: Linear variable x can occur only once in the right-hand-side of an assignment +typecheck.bpl(64,4): Error: Linear variable a can occur only once as an input parameter +typecheck.bpl(66,4): Error: Only variable can be passed to linear parameter b +typecheck.bpl(68,4): Error: The domains of formal and actual parameters must be the same +typecheck.bpl(70,4): Error: The domains of formal and actual parameters must be the same +typecheck.bpl(72,4): Error: Only a linear argument can be passed to linear parameter a +typecheck.bpl(77,4): Error: Linear variable a can occur only once as an input parameter of a parallel call +typecheck.bpl(86,0): Error: Output variable d must be available at a return +typecheck.bpl(95,0): Error: Global variable g must be available at a return +typecheck.bpl(100,7): Error: unavailable source for a linear read +typecheck.bpl(101,0): Error: Output variable r must be available at a return +15 type checking errors detected in typecheck.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linearity-bug-1.bpl boogie-2.4.1+dfsg/Test/civl/linearity-bug-1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linearity-bug-1.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linearity-bug-1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,36 @@ +// RUN: %boogie -noinfer -useArrayTheory -typeEncoding:m "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 0,1} n : int; +var {:linear "tid"} {:layer 0,1} S : TidSet; + +// This test exposed a bug in the commutativity checker. Since t is linear_in, +// it does not have to be available after the atomic action executed. However, +// the disjointness expression in the antecedent of the postcondition of the +// commutativity checker procedure contained t and thus made the whole +// postcondition 'vacuously' (because t is added to A) true. + +procedure {:atomic} {:layer 1} atomic_inc_n ({:linear_in "tid"} t : Tid) +modifies S, n; +{ assert !S[t]; S[t] := true; n := n + 1; } + +procedure {:yields} {:layer 0} {:refines "atomic_inc_n"} inc_n ({:linear_in "tid"} t : Tid); + +procedure {:right} {:layer 1} atomic_read_n () returns (ret : int) +{ ret := n; } + +procedure {:yields} {:layer 0} {:refines "atomic_read_n"} read_n () returns (ret : int); + +// ########################################################################### +// Linear permissions + +type Tid = int; +type TidSet = [Tid]bool; + +function {:builtin "MapConst"} MapConstBool (bool) : [Tid]bool; + +function {:inline} {:linear "tid"} TidCollector (x : Tid) : [Tid]bool +{ MapConstBool(false)[x := true] } + +function {:inline} {:linear "tid"} TidSetCollector (x : [Tid]bool) : [Tid]bool +{ x } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linearity-bug-1.bpl.expect boogie-2.4.1+dfsg/Test/civl/linearity-bug-1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linearity-bug-1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linearity-bug-1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,9 @@ +(0,0): Error BP5003: A postcondition might not hold on this return path. +linearity-bug-1.bpl(19,31): Related location: Commutativity check between atomic_read_n_1 and atomic_inc_n_1 failed +Execution trace: + linearity-bug-1.bpl(19,31): inline$atomic_read_n_1$0$Entry + linearity-bug-1.bpl(13,32): inline$atomic_inc_n_1$0$Entry + linearity-bug-1.bpl(15,3): inline$atomic_inc_n_1$0$anon0 + linearity-bug-1.bpl(13,32): inline$atomic_inc_n_1$0$Return + +Boogie program verifier finished with 1 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linearity-bug-2.bpl boogie-2.4.1+dfsg/Test/civl/linearity-bug-2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linearity-bug-2.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linearity-bug-2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,29 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:linear "lin"} {:layer 1,2} set : [int]bool; + +procedure {:atomic} {:layer 2} atomic_foo ({:linear "lin"} i : int) +modifies set; +{ set[i] := true; } + +procedure {:yields} {:layer 1} {:refines "atomic_foo"} foo ({:linear "lin"} i : int); + +procedure {:yields} {:layer 2} main ({:linear "lin"} i : int) +{ + yield; + call foo(i); + assert {:layer 2} false; + yield; +} + +// ########################################################################### +// Collectors for linear domains + +function {:builtin "MapConst"} MapConstBool (bool) : [int]bool; + +function {:inline} {:linear "lin"} Collector (x : int) : [int]bool +{ MapConstBool(false)[x := true] } + +function {:inline} {:linear "lin"} SetCollector (x : [int]bool) : [int]bool +{ x } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linearity-bug-2.bpl.expect boogie-2.4.1+dfsg/Test/civl/linearity-bug-2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linearity-bug-2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linearity-bug-2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,8 @@ +(0,0): Error BP5003: A postcondition might not hold on this return path. +linearity-bug-2.bpl(6,32): Related location: Linearity invariant for domain lin is not preserved by atomic_foo_2. +Execution trace: + linearity-bug-2.bpl(6,32): inline$atomic_foo_2$0$Entry + linearity-bug-2.bpl(8,10): inline$atomic_foo_2$0$anon0 + linearity-bug-2.bpl(6,32): inline$atomic_foo_2$0$Return + +Boogie program verifier finished with 2 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear_out_bug.bpl boogie-2.4.1+dfsg/Test/civl/linear_out_bug.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear_out_bug.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear_out_bug.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,37 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 0,2} {:linear "addr"} Addrs:[int]bool; + +// This test exposed an unsoundness due to linearity assumes. In particular, the +// linear_out parameter of RemoveAddr was still part of the disjointness +// expression in the post-state. + +procedure {:atomic} {:layer 1,2} AddAddr({:linear_in "addr"} i: int) +modifies Addrs; +{ + Addrs[i] := true; +} + +// Gate not preserved by itself +procedure {:left} {:layer 1} RemoveAddr_1({:linear_out "addr"} i: int) +modifies Addrs; +{ + assert Addrs[i]; + Addrs[i] := false; +} + +// Without the gate, RemoveAddr does not commute with AddAddr +procedure {:left} {:layer 2} RemoveAddr_2({:linear_out "addr"} i: int) +modifies Addrs; +{ + Addrs[i] := false; +} + +//////////////////////////////////////////////////////////////////////////////// + +function {:builtin "MapConst"} MapConstBool(bool) : [int]bool; +function {:inline} {:linear "addr"} AddrCollector(x: int) : [int]bool +{ MapConstBool(false)[x := true] } +function {:inline} {:linear "addr"} AddrsCollector(xs: [int]bool) : [int]bool +{ xs } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear_out_bug.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear_out_bug.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear_out_bug.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear_out_bug.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,22 @@ +(0,0): Error BP5003: A postcondition might not hold on this return path. +linear_out_bug.bpl(20,5): Related location: Gate of RemoveAddr_1_1 not preserved by RemoveAddr_1_1 +Execution trace: + linear_out_bug.bpl(17,30): inline$RemoveAddr_1_1$0$Entry + linear_out_bug.bpl(20,5): inline$RemoveAddr_1_1$0$anon0 + linear_out_bug.bpl(17,30): inline$RemoveAddr_1_1$0$Return +(0,0): Error BP5003: A postcondition might not hold on this return path. +linear_out_bug.bpl(10,34): Related location: Commutativity check between AddAddr_2 and RemoveAddr_2_2 failed +Execution trace: + linear_out_bug.bpl(10,34): inline$AddAddr_2$0$Entry + linear_out_bug.bpl(13,14): inline$AddAddr_2$0$anon0 + linear_out_bug.bpl(25,30): inline$RemoveAddr_2_2$0$Entry + linear_out_bug.bpl(28,14): inline$RemoveAddr_2_2$0$anon0 + linear_out_bug.bpl(25,30): inline$RemoveAddr_2_2$0$Return +(0,0): Error BP5003: A postcondition might not hold on this return path. +linear_out_bug.bpl(25,30): Related location: Linearity invariant for domain addr is not preserved by RemoveAddr_2_2. +Execution trace: + linear_out_bug.bpl(25,30): inline$RemoveAddr_2_2$0$Entry + linear_out_bug.bpl(28,14): inline$RemoveAddr_2_2$0$anon0 + linear_out_bug.bpl(25,30): inline$RemoveAddr_2_2$0$Return + +Boogie program verifier finished with 8 verified, 3 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear-set2.bpl boogie-2.4.1+dfsg/Test/civl/linear-set2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear-set2.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear-set2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -4,6 +4,7 @@ function {:builtin "MapConst"} MapConstInt(int) : [X]int; function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; function {:builtin "MapOr"} MapOr([X]bool, [X]bool) : [X]bool; +function {:builtin "MapAnd"} MapAnd([X]bool, [X]bool) : [X]bool; function {:inline} None() : [X]bool { @@ -19,11 +20,21 @@ { xs } +function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool +{ + MapConstBool(false)[x := true] +} +function {:inline} {:linear "tid"} TidSetCollector(x: [X]bool) : [X]bool +{ + x +} var {:layer 0,1} x: int; var {:layer 0,1} l: X; const nil: X; +var {:layer 0,1}{:linear "tid"} unallocated:[X]bool; + procedure {:yields} {:layer 1} Split({:linear_in "x"} xls: [X]bool) returns ({:linear "x"} xls1: [X]bool, {:linear "x"} xls2: [X]bool) ensures {:layer 1} xls == MapOr(xls1, xls2) && xls1 != None() && xls2 != None(); { @@ -40,22 +51,42 @@ yield; } -procedure {:yields} {:layer 0,1} Set(v: int); -ensures {:atomic} |{A: x := v; return true; }|; +procedure {:atomic} {:layer 1} AtomicSet(v: int) +modifies x; +{ x := v; } + +procedure {:yields} {:layer 0} {:refines "AtomicSet"} Set(v: int); + +procedure {:atomic} {:layer 1} AtomicLock(tidls: X) +modifies l; +{ assume l == nil; l := tidls; } -procedure {:yields} {:layer 0,1} Lock(tidls: X); -ensures {:atomic} |{A: assume l == nil; l := tidls; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicLock"} Lock(tidls: X); -procedure {:yields} {:layer 0,1} Unlock(); -ensures {:atomic} |{A: l := nil; return true; }|; +procedure {:atomic} {:layer 1} AtomicUnlock() +modifies l; +{ l := nil; } -procedure {:yields} {:layer 0,1} SplitLow({:linear_in "x"} xls: [X]bool) returns ({:linear "x"} xls1: [X]bool, {:linear "x"} xls2: [X]bool); -ensures {:atomic} |{ A: assume xls == MapOr(xls1, xls2) && xls1 != None() && xls2 != None(); return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicUnlock"} Unlock(); -procedure {:yields} {:layer 0,1} AllocateLow() returns ({:linear "tid"} xls: X); -ensures {:atomic} |{ A: assume xls != nil; return true; }|; +procedure {:atomic} {:layer 1} AtomicSplitLow({:linear_in "x"} xls: [X]bool) returns ({:linear "x"} xls1: [X]bool, {:linear "x"} xls2: [X]bool) +{ + // xls == xls1 ⊎ xls2 + assume xls == MapOr(xls1, xls2); + assume MapAnd(xls1, xls2) == None(); + assume xls1 != None(); + assume xls2 != None(); +} + +procedure {:yields} {:layer 0} {:refines "AtomicSplitLow"} SplitLow({:linear_in "x"} xls: [X]bool) returns ({:linear "x"} xls1: [X]bool, {:linear "x"} xls2: [X]bool); + +procedure {:atomic} {:layer 1} AtomicAllocateLow() returns ({:linear "tid"} xls: X) +modifies unallocated; +{ assume xls != nil && unallocated[xls]; unallocated[xls] := false; } -procedure {:yields} {:layer 1} main({:linear_in "tid"} tidls': X, {:linear_in "x"} xls': [X]bool) +procedure {:yields} {:layer 0} {:refines "AtomicAllocateLow"} AllocateLow() returns ({:linear "tid"} xls: X); + +procedure {:yields} {:layer 1} main({:linear_in "tid"} tidls': X, {:linear_in "x"} xls': [X]bool) requires {:layer 1} tidls' != nil && xls' == All(); { var {:linear "tid"} tidls: X; @@ -99,8 +130,6 @@ yield; assert {:layer 1} tidls != nil && xls != None(); assert {:layer 1} x == 0; - yield; - assert {:layer 1} tidls != nil && xls != None(); call Unlock(); yield; } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear-set2.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear-set2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear-set2.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear-set2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 8 verified, 0 errors +Boogie program verifier finished with 10 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear-set.bpl boogie-2.4.1+dfsg/Test/civl/linear-set.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear-set.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear-set.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -4,6 +4,7 @@ function {:builtin "MapConst"} MapConstInt(int) : [X]int; function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; function {:builtin "MapOr"} MapOr([X]bool, [X]bool) : [X]bool; +function {:builtin "MapAnd"} MapAnd([X]bool, [X]bool) : [X]bool; function {:inline} None() : [X]bool { @@ -19,6 +20,14 @@ { xs } +function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool +{ + MapConstBool(false)[x := true] +} +function {:inline} {:linear "tid"} TidSetCollector(x: [X]bool) : [X]bool +{ + x +} var {:layer 0,1} x: int; var {:layer 0,1} l: [X]bool; @@ -39,22 +48,38 @@ yield; } -procedure {:yields} {:layer 0,1} Set(v: int); -ensures {:atomic} |{A: x := v; return true; }|; +procedure {:atomic} {:layer 1} AtomicSet(v: int) +modifies x; +{ x := v; } + +procedure {:yields} {:layer 0} {:refines "AtomicSet"} Set(v: int); + +procedure {:atomic} {:layer 1} AtomicLock(tidls: [X]bool) +modifies l; +{ assume l == None(); l := tidls; } -procedure {:yields} {:layer 0,1} Lock(tidls: [X]bool); -ensures {:atomic} |{A: assume l == None(); l := tidls; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicLock"} Lock(tidls: [X]bool); -procedure {:yields} {:layer 0,1} Unlock(); -ensures {:atomic} |{A: l := None(); return true; }|; +procedure {:atomic} {:layer 1} AtomicUnlock() +modifies l; +{ l := None(); } -procedure {:yields} {:layer 0,1} SplitLow({:linear_in "x"} xls: [X]bool) returns ({:linear "x"} xls1: [X]bool, {:linear "x"} xls2: [X]bool); -ensures {:atomic} |{ A: assume xls == MapOr(xls1, xls2) && xls1 != None() && xls2 != None(); return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicUnlock"} Unlock(); -procedure {:yields} {:layer 0,1} AllocateLow() returns ({:linear "tid"} xls: [X]bool); -ensures {:atomic} |{ A: return true; }|; +procedure {:atomic} {:layer 1} AtomicSplitLow({:linear_in "x"} xls: [X]bool) returns ({:linear "x"} xls1: [X]bool, {:linear "x"} xls2: [X]bool) +{ + // xls == xls1 ⊎ xls2 + assume xls == MapOr(xls1, xls2); + assume MapAnd(xls1, xls2) == None(); + assume xls1 != None(); + assume xls2 != None(); +} + +procedure {:yields} {:layer 0} {:refines "AtomicSplitLow"} SplitLow({:linear_in "x"} xls: [X]bool) returns ({:linear "x"} xls1: [X]bool, {:linear "x"} xls2: [X]bool); -procedure {:yields} {:layer 1} main({:linear_in "tid"} tidls': [X]bool, {:linear_in "x"} xls': [X]bool) +procedure {:yields} {:layer 0} AllocateLow() returns ({:linear "tid"} xls: [X]bool); + +procedure {:yields} {:layer 1} main({:linear_in "tid"} tidls': [X]bool, {:linear_in "x"} xls': [X]bool) requires {:layer 1} tidls' != None() && xls' == All(); { var {:linear "tid"} tidls: [X]bool; @@ -65,6 +90,7 @@ tidls := tidls'; xls := xls'; + yield; call Set(42); yield; @@ -99,7 +125,6 @@ yield; assert {:layer 1} tidls != None() && xls != None(); assert {:layer 1} x == 0; - assert {:layer 1} tidls != None() && xls != None(); call Unlock(); yield; } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear-set.bpl.expect boogie-2.4.1+dfsg/Test/civl/linear-set.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/linear-set.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/linear-set.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 8 verified, 0 errors +Boogie program verifier finished with 9 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lipton.bpl boogie-2.4.1+dfsg/Test/civl/lipton.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lipton.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lipton.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,27 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// The semaphor operations P and V used in [Lipton 1975] + +var {:layer 0,1} c : int; + +procedure {:yields}{:layer 1} Thread () +{ + yield; + call p(); + call p(); + call v(); + call v(); + yield; +} + +procedure {:right} {:layer 1} P () +modifies c; +{ assume c > 0; c := c - 1; } + +procedure {:left} {:layer 1} V () +modifies c; +{ c := c + 1; } + +procedure {:yields}{:layer 0}{:refines "P"} p (); +procedure {:yields}{:layer 0}{:refines "V"} v (); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lipton.bpl.expect boogie-2.4.1+dfsg/Test/civl/lipton.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lipton.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lipton.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 3 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lit.local.cfg boogie-2.4.1+dfsg/Test/civl/lit.local.cfg --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lit.local.cfg 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lit.local.cfg 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1 @@ +config.excludes = ['GC.bpl', 'reserve.bpl'] diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lock2.bpl boogie-2.4.1+dfsg/Test/civl/lock2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lock2.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lock2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -16,48 +16,60 @@ procedure {:yields} {:layer 2} Customer() { yield; - while (*) + while (*) { call Enter(); - yield; - call Leave(); + yield; + call Leave(); yield; } yield; } -procedure {:yields} {:layer 1,2} Enter() -ensures {:atomic} |{ A: assume b == 0; b := 1; return true; }|; +procedure {:atomic} {:layer 2} AtomicEnter() +modifies b; +{ assume b == 0; b := 1; } + +procedure {:yields} {:layer 1} {:refines "AtomicEnter"} Enter() { var _old, curr: int; yield; - while (true) { + while (true) { call _old := CAS(0, 1); - yield; + yield; if (_old == 0) { - break; - } - while (true) { - call curr := Read(); - yield; - if (curr == 0) { - break; - } - } - yield; + break; + } + while (true) { + call curr := Read(); + yield; + if (curr == 0) { + break; + } + } + yield; } yield; } -procedure {:yields} {:layer 0,2} Read() returns (val: int); -ensures {:atomic} |{ A: val := b; return true; }|; +procedure {:atomic} {:layer 1,2} AtomicRead() returns (val: int) +{ val := b; } + +procedure {:yields} {:layer 0} {:refines "AtomicRead"} Read() returns (val: int); + +procedure {:atomic} {:layer 1,2} AtomicCAS(prev: int, next: int) returns (_old: int) +modifies b; +{ + _old := b; + if (_old == prev) { + b := next; + } +} + +procedure {:yields} {:layer 0} {:refines "AtomicCAS"} CAS(prev: int, next: int) returns (_old: int); -procedure {:yields} {:layer 0,2} CAS(prev: int, next: int) returns (_old: int); -ensures {:atomic} |{ -A: _old := b; goto B, C; -B: assume _old == prev; b := next; return true; -C: assume _old != prev; return true; -}|; +procedure {:atomic} {:layer 1,2} AtomicLeave() +modifies b; +{ b := 0; } -procedure {:yields} {:layer 0,2} Leave(); -ensures {:atomic} |{ A: b := 0; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicLeave"} Leave(); \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lock.bpl boogie-2.4.1+dfsg/Test/civl/lock.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lock.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lock.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -16,42 +16,50 @@ procedure {:yields} {:layer 2} Customer() { yield; - while (*) + while (*) { call Enter(); - yield; - call Leave(); + yield; + call Leave(); yield; } yield; } -procedure {:yields} {:layer 1,2} Enter() -ensures {:atomic} |{ A: assume !b; b := true; return true; }|; +procedure {:atomic} {:layer 2} AtomicEnter() +modifies b; +{ assume !b; b := true; } + +procedure {:yields} {:layer 1} {:refines "AtomicEnter"} Enter() { var status: bool; yield; - L: + while (true) { call status := CAS(false, true); - yield; - goto A, B; + if (status) { + yield; + return; + } + yield; + } + yield; +} - A: - assume status; - yield; - return; - - B: - assume !status; - goto L; +procedure {:atomic} {:layer 1,2} AtomicCAS(prev: bool, next: bool) returns (status: bool) +modifies b; +{ + if (b == prev) { + b := next; + status := true; + } else { + status := false; + } } -procedure {:yields} {:layer 0,2} CAS(prev: bool, next: bool) returns (status: bool); -ensures {:atomic} |{ -A: goto B, C; -B: assume b == prev; b := next; status := true; return true; -C: status := false; return true; -}|; +procedure {:yields} {:layer 0} {:refines "AtomicCAS"} CAS(prev: bool, next: bool) returns (status: bool); + +procedure {:atomic} {:layer 1,2} AtomicLeave() +modifies b; +{ b := false; } -procedure {:yields} {:layer 0,2} Leave(); -ensures {:atomic} |{ A: b := false; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicLeave"} Leave(); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lock-introduced.bpl boogie-2.4.1+dfsg/Test/civl/lock-introduced.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lock-introduced.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lock-introduced.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -1,110 +1,121 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; -function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool -{ - MapConstBool(false)[x := true] -} type X; const nil: X; + var {:layer 0,2} b: bool; var {:layer 1,3} lock: X; procedure {:yields} {:layer 3} Customer({:linear "tid"} tid: X) -requires {:layer 2} tid != nil; +requires {:layer 2} tid != nil; requires {:layer 2} InvLock(lock, b); ensures {:layer 2} InvLock(lock, b); { + yield; + assert {:layer 2} InvLock(lock, b); + while (*) + invariant {:layer 2} InvLock(lock, b); + { + call Enter(tid); + call Leave(tid); yield; assert {:layer 2} InvLock(lock, b); - while (*) - invariant {:layer 2} InvLock(lock, b); - { - call Enter(tid); - call Leave(tid); - yield; - assert {:layer 2} InvLock(lock, b); - } - yield; - assert {:layer 2} InvLock(lock, b); + } + yield; + assert {:layer 2} InvLock(lock, b); } function {:inline} InvLock(lock: X, b: bool) : bool -{ - lock != nil <==> b -} +{ lock != nil <==> b } -procedure {:yields} {:layer 2,3} Enter({:linear "tid"} tid: X) -requires {:layer 2} tid != nil; -requires {:layer 2} InvLock(lock, b); -ensures {:layer 2} InvLock(lock, b); -ensures {:right} |{ A: assume lock == nil && tid != nil; lock := tid; return true; }|; -{ - yield; - assert {:layer 2} InvLock(lock, b); - call LowerEnter(tid); - yield; - assert {:layer 2} InvLock(lock, b); -} +procedure {:right} {:layer 3} AtomicEnter({:linear "tid"} tid: X) +modifies lock; +{ assume lock == nil && tid != nil; lock := tid; } -procedure {:yields} {:layer 2,3} Leave({:linear "tid"} tid:X) +procedure {:yields} {:layer 2} {:refines "AtomicEnter"} Enter({:linear "tid"} tid: X) +requires {:layer 2} tid != nil; requires {:layer 2} InvLock(lock, b); ensures {:layer 2} InvLock(lock, b); -ensures {:atomic} |{ A: assert lock == tid && tid != nil; lock := nil; return true; }|; { - yield; - assert {:layer 2} InvLock(lock, b); - call LowerLeave(); - yield; - assert {:layer 2} InvLock(lock, b); + yield; + assert {:layer 2} InvLock(lock, b); + call LowerEnter(tid); + yield; + assert {:layer 2} InvLock(lock, b); } -procedure {:yields} {:layer 1,2} LowerEnter({:linear "tid"} tid: X) -ensures {:atomic} |{ A: assume !b; b := true; lock := tid; return true; }|; -{ - var status: bool; - yield; - L: - call status := CAS(false, true); - if (status) { - call SetLock(tid); - } - yield; - goto A, B; - - A: - assume status; - yield; - return; - - B: - assume !status; - goto L; -} +procedure {:left} {:layer 3} AtomicLeave({:linear "tid"} tid:X) +modifies lock; +{ assert lock == tid && tid != nil; lock := nil; } -procedure {:yields} {:layer 1,2} LowerLeave() -ensures {:atomic} |{ A: b := false; lock := nil; return true; }|; +procedure {:yields} {:layer 2} {:refines "AtomicLeave"} Leave({:linear "tid"} tid:X) +requires {:layer 2} InvLock(lock, b); +ensures {:layer 2} InvLock(lock, b); { - yield; - call SET(false); - call SetLock(nil); - yield; + yield; + assert {:layer 2} InvLock(lock, b); + call LowerLeave(); + yield; + assert {:layer 2} InvLock(lock, b); +} + +procedure {:atomic} {:layer 2} AtomicLowerEnter({:linear "tid"} tid: X) +modifies b, lock; +{ assume !b; b := true; lock := tid; } + +procedure {:yields} {:layer 1} {:refines "AtomicLowerEnter"} LowerEnter({:linear "tid"} tid: X) +{ + var status: bool; + yield; + while (true) { + call status := CAS(false, true); + if (status) { + call SetLock(tid); + yield; + return; + } + yield; + } + yield; +} + +procedure {:atomic} {:layer 2} AtomicLowerLeave() +modifies b, lock; +{ b := false; lock := nil; } + +procedure {:yields} {:layer 1} {:refines "AtomicLowerLeave"} LowerLeave() +{ + yield; + call SET(false); + call SetLock(nil); + yield; } procedure {:layer 1} {:inline 1} SetLock(v: X) modifies lock; +{ lock := v; } + +procedure {:atomic} {:layer 1} AtomicCAS(prev: bool, next: bool) returns (status: bool) +modifies b; { - lock := v; + if (b == prev) { + b := next; + status := true; + } else { + status := false; + } } -procedure {:yields} {:layer 0,1} CAS(prev: bool, next: bool) returns (status: bool); -ensures {:atomic} |{ -A: goto B, C; -B: assume b == prev; b := next; status := true; return true; -C: status := false; return true; -}|; +procedure {:atomic} {:layer 1} AtomicSET(next: bool) +modifies b; +{ b := next; } -procedure {:yields} {:layer 0,1} SET(next: bool); -ensures {:atomic} |{ A: b := next; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicCAS"} CAS(prev: bool, next: bool) returns (status: bool); +procedure {:yields} {:layer 0} {:refines "AtomicSET"} SET(next: bool); +function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; +function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool +{ + MapConstBool(false)[x := true] +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lock-introduced.bpl.expect boogie-2.4.1+dfsg/Test/civl/lock-introduced.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/lock-introduced.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/lock-introduced.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 17 verified, 0 errors +Boogie program verifier finished with 23 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/multiset.bpl boogie-2.4.1+dfsg/Test/civl/multiset.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/multiset.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/multiset.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -6,10 +6,10 @@ const unique nil: X; const unique done: X; -var {:layer 0} elt : [int]int; -var {:layer 0} valid : [int]bool; -var {:layer 0} lock : [int]X; -var {:layer 0} owner : [int]X; +var {:layer 0,3} elt : [int]int; +var {:layer 0,3} valid : [int]bool; +var {:layer 0,3} lock : [int]X; +var {:layer 0,3} owner : [int]X; const max : int; function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; @@ -20,305 +20,291 @@ axiom (max > 0); -procedure {:yields} {:layer 0} acquire(i : int, {:linear "tid"} tid: X); -ensures {:right} |{ A: - assert 0 <= i && i < max; - assert tid != nil && tid != done; - assume lock[i] == nil; - lock[i] := tid; - return true; - }|; - - -procedure {:yields} {:layer 0} release(i : int, {:linear "tid"} tid: X); -ensures {:left} |{ A: - assert 0 <= i && i < max; - assert lock[i] == tid; - assert tid != nil && tid != done; - lock[i] := nil; - return true; - }|; - - -procedure {:yields} {:layer 0,1} getElt(j : int, {:linear "tid"} tid: X) returns (elt_j:int); -ensures {:both} |{ A: - assert 0 <= j && j < max; - assert lock[j] == tid; - assert tid != nil && tid != done; - elt_j := elt[j]; - return true; - }|; - - -procedure {:yields} {:layer 0,1} setElt(j : int, x : int, {:linear "tid"} tid: X); -ensures {:both} |{ A: - assert x != null; - assert owner[j] == nil; - assert 0 <= j && j < max; - assert lock[j] == tid; - assert tid != nil && tid != done; - elt[j] := x; - owner[j] := tid; - return true; - }|; - - -procedure {:yields} {:layer 0,2} setEltToNull(j : int, {:linear "tid"} tid: X); -ensures {:left} |{ A: - assert owner[j] == tid; - assert 0 <= j && j < max; - assert lock[j] == tid; - assert !valid[j]; - assert tid != nil && tid != done; - elt[j] := null; - owner[j] := nil; - return true; - }|; - -procedure {:yields} {:layer 0,2} setValid(j : int, {:linear "tid"} tid: X); -ensures {:both} |{ A: - assert 0 <= j && j < max; - assert lock[j] == tid; - assert tid != nil && tid != done; - assert owner[j] == tid; - valid[j] := true; - owner[j] := done; - return true; - }|; - -procedure {:yields} {:layer 0,2} isEltThereAndValid(j : int, x : int, {:linear "tid"} tid: X) returns (fnd:bool); -ensures {:both} |{ A: - assert 0 <= j && j < max; - assert lock[j] == tid; - assert tid != nil && tid != done; - fnd := (elt[j] == x) && valid[j]; - return true; - }|; +procedure {:right} {:layer 1,2} atomic_acquire(i : int, {:linear "tid"} tid: X) +modifies lock; +{ assert 0 <= i && i < max; assert tid != nil && tid != done; assume lock[i] == nil; lock[i] := tid; } -procedure {:yields} {:layer 1,2} FindSlot(x : int, {:linear "tid"} tid: X) returns (r : int) -requires {:layer 1} Inv(valid, elt, owner) && x != null && tid != nil && tid != done; +procedure {:yields} {:layer 0} {:refines "atomic_acquire"} acquire(i : int, {:linear "tid"} tid: X); + +procedure {:left} {:layer 1,2} atomic_release(i : int, {:linear "tid"} tid: X) +modifies lock; +{ assert 0 <= i && i < max; assert lock[i] == tid; assert tid != nil && tid != done; lock[i] := nil; } + +procedure {:yields} {:layer 0} {:refines "atomic_release"} release(i : int, {:linear "tid"} tid: X); + +procedure {:both} {:layer 1} atomic_getElt(j : int, {:linear "tid"} tid: X) returns (elt_j:int) +{ assert 0 <= j && j < max; assert tid != nil && tid != done; assert lock[j] == tid; elt_j := elt[j]; } + +procedure {:yields} {:layer 0} {:refines "atomic_getElt"} getElt(j : int, {:linear "tid"} tid: X) returns (elt_j:int); + +procedure {:both} {:layer 1} atomic_setElt(j : int, x : int, {:linear "tid"} tid: X) +modifies elt, owner; +{ assert x != null && owner[j] == nil; assert 0 <= j && j < max; assert lock[j] == tid; assert tid != nil && tid != done; elt[j] := x; owner[j] := tid; } + +procedure {:yields} {:layer 0} {:refines "atomic_setElt"} setElt(j : int, x : int, {:linear "tid"} tid: X); + +procedure {:left} {:layer 1,2} atomic_setEltToNull(j : int, {:linear "tid"} tid: X) +modifies elt, owner; +{ assert owner[j] == tid && lock[j] == tid; assert 0 <= j && j < max; assert !valid[j]; assert tid != nil && tid != done; elt[j] := null; owner[j] := nil; } + +procedure {:yields} {:layer 0} {:refines "atomic_setEltToNull"} setEltToNull(j : int, {:linear "tid"} tid: X); + +procedure {:both} {:layer 1,2} atomic_setValid(j : int, {:linear "tid"} tid: X) +modifies valid, owner; +{ assert 0 <= j && j < max; assert lock[j] == tid; assert tid != nil && tid != done; assert owner[j] == tid; valid[j] := true; owner[j] := done; } + +procedure {:yields} {:layer 0} {:refines "atomic_setValid"} setValid(j : int, {:linear "tid"} tid: X); + +procedure {:both} {:layer 1,2} atomic_isEltThereAndValid(j : int, x : int, {:linear "tid"} tid: X) returns (fnd:bool) +{ assert 0 <= j && j < max; assert lock[j] == tid; assert tid != nil && tid != done; fnd := (elt[j] == x) && valid[j]; } + +procedure {:yields} {:layer 0} {:refines "atomic_isEltThereAndValid"} isEltThereAndValid(j : int, x : int, {:linear "tid"} tid: X) returns (fnd:bool); + +procedure {:right} {:layer 2} AtomicFindSlot(x : int, {:linear "tid"} tid: X) returns (r : int) +modifies elt, owner; +{ + assert tid != nil && tid != done; + assert x != null; + if (*) { + assume (0 <= r && r < max); + assume elt[r] == null; + assume owner[r] == nil; + assume !valid[r]; + elt[r] := x; + owner[r] := tid; + } else { + assume (r == -1); + } +} + +procedure {:yields} {:layer 1} {:refines "AtomicFindSlot"} FindSlot(x : int, {:linear "tid"} tid: X) returns (r : int) +requires {:layer 1} Inv(valid, elt, owner) && x != null && tid != nil && tid != done; ensures {:layer 1} Inv(valid, elt, owner); -ensures {:right} |{ A: assert tid != nil && tid != done; - assert x != null; - goto B, C; - B: assume (0 <= r && r < max); - assume elt[r] == null; - assume owner[r] == nil; - assume !valid[r]; - elt[r] := x; - owner[r] := tid; - return true; - C: assume (r == -1); return true; - }|; -{ - var j : int; - var elt_j : int; - - par Yield1(); - - j := 0; - while(j < max) - invariant {:layer 1} Inv(valid, elt, owner); - invariant {:layer 1} 0 <= j; - { - call acquire(j, tid); - call elt_j := getElt(j, tid); - if(elt_j == null) - { - call setElt(j, x, tid); - call release(j, tid); - r := j; - - par Yield1(); - return; - } - call release(j,tid); - - par Yield1(); - - j := j + 1; - } - r := -1; +{ + var j : int; + var elt_j : int; - par Yield1(); - return; + par Yield1(); + + j := 0; + while(j < max) + invariant {:layer 1} Inv(valid, elt, owner); + invariant {:layer 1} 0 <= j; + { + call acquire(j, tid); + call elt_j := getElt(j, tid); + if(elt_j == null) + { + call setElt(j, x, tid); + call release(j, tid); + r := j; + + par Yield1(); + return; + } + call release(j,tid); + + par Yield1(); + + j := j + 1; + } + r := -1; + + par Yield1(); + return; +} + +procedure {:atomic} {:layer 3} AtomicInsert(x : int, {:linear "tid"} tid: X) returns (result : bool) +modifies elt, valid, owner; +{ + var r:int; + if (*) { + assume (0 <= r && r < max); + assume valid[r] == false; + assume elt[r] == null; + assume owner[r] == nil; + elt[r] := x; + valid[r] := true; + owner[r] := done; + result := true; + } else { + result := false; + } } -procedure {:yields} {:layer 2} Insert(x : int, {:linear "tid"} tid: X) returns (result : bool) -requires {:layer 1} Inv(valid, elt, owner) && x != null && tid != nil && tid != done; +procedure {:yields} {:layer 2} {:refines "AtomicInsert"} Insert(x : int, {:linear "tid"} tid: X) returns (result : bool) +requires {:layer 1} Inv(valid, elt, owner) && x != null && tid != nil && tid != done; ensures {:layer 1} Inv(valid, elt, owner); -requires {:layer 2} Inv(valid, elt, owner) && x != null && tid != nil && tid != done; +requires {:layer 2} Inv(valid, elt, owner) && x != null && tid != nil && tid != done; ensures {:layer 2} Inv(valid, elt, owner); -ensures {:atomic} |{ var r:int; - A: goto B, C; - B: assume (0 <= r && r < max); - assume valid[r] == false; - assume elt[r] == null; - assume owner[r] == nil; - elt[r] := x; valid[r] := true; owner[r] := done; - result := true; return true; - C: result := false; return true; - }|; - { - var i: int; - par Yield12(); - call i := FindSlot(x, tid); - - if(i == -1) - { - result := false; - par Yield12(); - return; - } - par Yield1(); - assert {:layer 1} i != -1; - assert {:layer 2} i != -1; - call acquire(i, tid); - assert {:layer 2} elt[i] == x; - assert {:layer 2} valid[i] == false; - call setValid(i, tid); - call release(i, tid); - result := true; - par Yield12(); - return; +{ + var i: int; + par Yield12(); + call i := FindSlot(x, tid); + + if(i == -1) + { + result := false; + par Yield12(); + return; + } + par Yield1(); + assert {:layer 1} i != -1; + assert {:layer 2} i != -1; + call acquire(i, tid); + assert {:layer 2} elt[i] == x; + assert {:layer 2} valid[i] == false; + call setValid(i, tid); + call release(i, tid); + result := true; + par Yield12(); + return; +} + +procedure {:atomic} {:layer 3} AtomicInsertPair(x : int, y : int, {:linear "tid"} tid: X) returns (result : bool) +modifies elt, valid, owner; +{ + var rx:int; + var ry:int; + if (*) { + assume (0 <= rx && rx < max && 0 <= ry && ry < max && rx != ry); + assume valid[rx] == false; + assume valid[ry] == false; + assume elt[rx] == null; + assume elt[rx] == null; + elt[rx] := x; + elt[ry] := y; + valid[rx] := true; + valid[ry] := true; + owner[rx] := done; + owner[ry] := done; + result := true; + } else { + result := false; + } } -procedure {:yields} {:layer 2} InsertPair(x : int, y : int, {:linear "tid"} tid: X) returns (result : bool) -requires {:layer 1} Inv(valid, elt, owner) && x != null && y != null && tid != nil && tid != done; +procedure {:yields} {:layer 2} {:refines "AtomicInsertPair"} InsertPair(x : int, y : int, {:linear "tid"} tid: X) returns (result : bool) +requires {:layer 1} Inv(valid, elt, owner) && x != null && y != null && tid != nil && tid != done; ensures {:layer 1} Inv(valid, elt, owner); -requires {:layer 2} Inv(valid, elt, owner) && x != null && y != null && tid != nil && tid != done; +requires {:layer 2} Inv(valid, elt, owner) && x != null && y != null && tid != nil && tid != done; ensures {:layer 2} Inv(valid, elt, owner); -ensures {:atomic} |{ var rx:int; - var ry:int; - A: goto B, C; - B: assume (0 <= rx && rx < max && 0 <= ry && ry < max && rx != ry); - assume valid[rx] == false; - assume valid[ry] == false; - assume elt[rx] == null; - assume elt[rx] == null; - elt[rx] := x; - elt[ry] := y; - valid[rx] := true; - valid[ry] := true; - owner[rx] := done; - owner[ry] := done; - result := true; return true; - C: result := false; return true; - }|; - { - var i : int; - var j : int; - par Yield12(); - - call i := FindSlot(x, tid); - - if (i == -1) - { - result := false; - par Yield12(); - return; - } - - par Yield1(); - call j := FindSlot(y, tid); - - if(j == -1) - { - par Yield1(); - call acquire(i,tid); - call setEltToNull(i, tid); - call release(i,tid); - result := false; - par Yield12(); - return; - } - - par Yield1(); - assert {:layer 2} i != -1 && j != -1; - call acquire(i, tid); - call acquire(j, tid); - assert {:layer 2} elt[i] == x; - assert {:layer 2} elt[j] == y; - assert {:layer 2} valid[i] == false; - assert {:layer 2} valid[j] == false; - call setValid(i, tid); - call setValid(j, tid); - call release(j, tid); - call release(i, tid); - result := true; - par Yield12(); - return; +{ + var i : int; + var j : int; + par Yield12(); + + call i := FindSlot(x, tid); + + if (i == -1) + { + result := false; + par Yield12(); + return; + } + + par Yield1(); + call j := FindSlot(y, tid); + + if(j == -1) + { + par Yield1(); + call acquire(i,tid); + call setEltToNull(i, tid); + call release(i,tid); + result := false; + par Yield12(); + return; + } + + par Yield1(); + assert {:layer 2} i != -1 && j != -1; + call acquire(i, tid); + call acquire(j, tid); + assert {:layer 2} elt[i] == x; + assert {:layer 2} elt[j] == y; + assert {:layer 2} valid[i] == false; + assert {:layer 2} valid[j] == false; + call setValid(i, tid); + call setValid(j, tid); + call release(j, tid); + call release(i, tid); + result := true; + par Yield12(); + return; +} + +procedure {:atomic} {:layer 3} AtomicLookUp(x : int, {:linear "tid"} tid: X, old_valid:[int]bool, old_elt:[int]int) returns (found : bool) +{ + assert tid != nil && tid != done; + assert x != null; + assume found ==> (exists ii:int :: 0 <= ii && ii < max && valid[ii] && elt[ii] == x); + assume !found ==> (forall ii:int :: 0 <= ii && ii < max ==> !(old_valid[ii] && old_elt[ii] == x)); } -procedure {:yields} {:layer 2} LookUp(x : int, {:linear "tid"} tid: X, old_valid:[int]bool, old_elt:[int]int) returns (found : bool) +procedure {:yields} {:layer 2} {:refines "AtomicLookUp"} LookUp(x : int, {:linear "tid"} tid: X, old_valid:[int]bool, old_elt:[int]int) returns (found : bool) requires {:layer 1} {:layer 2} old_valid == valid && old_elt == elt; requires {:layer 1} {:layer 2} Inv(valid, elt, owner); requires {:layer 1} {:layer 2} (tid != nil && tid != done); ensures {:layer 1} {:layer 2} Inv(valid, elt, owner); -ensures {:atomic} |{ A: assert tid != nil && tid != done; - assert x != null; - assume found ==> (exists ii:int :: 0 <= ii && ii < max && valid[ii] && elt[ii] == x); - assume !found ==> (forall ii:int :: 0 <= ii && ii < max ==> !(old_valid[ii] && old_elt[ii] == x)); - return true; - }|; -{ - var j : int; - var isThere : bool; - - par Yield12() | YieldLookUp(old_valid, old_elt); - - j := 0; - - while(j < max) - invariant {:layer 1} {:layer 2} Inv(valid, elt, owner); - invariant {:layer 1} {:layer 2} (forall ii:int :: 0 <= ii && ii < j ==> !(old_valid[ii] && old_elt[ii] == x)); - invariant {:layer 1} {:layer 2} (forall ii:int :: 0 <= ii && ii < max && old_valid[ii] ==> valid[ii] && old_elt[ii] == elt[ii]); - invariant {:layer 1} {:layer 2} 0 <= j; - { - call acquire(j, tid); - call isThere := isEltThereAndValid(j, x, tid); - if(isThere) - { - call release(j, tid); - found := true; - par Yield12() | YieldLookUp(old_valid, old_elt); - return; - } - call release(j,tid); - par Yield12() | YieldLookUp(old_valid, old_elt); - j := j + 1; - } - found := false; +{ + var j : int; + var isThere : bool; + + par Yield12() | YieldLookUp(old_valid, old_elt); + + j := 0; + + while(j < max) + invariant {:layer 1} {:layer 2} Inv(valid, elt, owner); + invariant {:layer 1} {:layer 2} (forall ii:int :: 0 <= ii && ii < j ==> !(old_valid[ii] && old_elt[ii] == x)); + invariant {:layer 1} {:layer 2} (forall ii:int :: 0 <= ii && ii < max && old_valid[ii] ==> valid[ii] && old_elt[ii] == elt[ii]); + invariant {:layer 1} {:layer 2} 0 <= j; + { + call acquire(j, tid); + call isThere := isEltThereAndValid(j, x, tid); + if(isThere) + { + call release(j, tid); + found := true; + par Yield12() | YieldLookUp(old_valid, old_elt); + return; + } + call release(j,tid); + par Yield12() | YieldLookUp(old_valid, old_elt); + j := j + 1; + } + found := false; - par Yield12() | YieldLookUp(old_valid, old_elt); - return; + par Yield12() | YieldLookUp(old_valid, old_elt); + return; } procedure {:yields} {:layer 1} Yield1() requires {:layer 1} Inv(valid, elt, owner); ensures {:layer 1} Inv(valid, elt, owner); { - yield; - assert {:layer 1} Inv(valid, elt, owner); + yield; + assert {:layer 1} Inv(valid, elt, owner); } procedure {:yields} {:layer 2} Yield12() requires {:layer 1} {:layer 2} Inv(valid, elt, owner); ensures {:layer 1} {:layer 2} Inv(valid, elt, owner); { - yield; - assert {:layer 1} {:layer 2} Inv(valid, elt, owner); + yield; + assert {:layer 1} {:layer 2} Inv(valid, elt, owner); } function {:inline} Inv(valid: [int]bool, elt: [int]int, owner: [int]X): (bool) { - (forall i:int :: 0 <= i && i < max ==> (elt[i] == null <==> (!valid[i] && owner[i] == nil))) + (forall i:int :: 0 <= i && i < max ==> (elt[i] == null <==> (!valid[i] && owner[i] == nil))) } procedure {:yields} {:layer 2} YieldLookUp(old_valid: [int]bool, old_elt: [int]int) requires {:layer 1} {:layer 2} (forall ii:int :: 0 <= ii && ii < max && old_valid[ii] ==> valid[ii] && old_elt[ii] == elt[ii]); ensures {:layer 1} {:layer 2} (forall ii:int :: 0 <= ii && ii < max && old_valid[ii] ==> valid[ii] && old_elt[ii] == elt[ii]); { - yield; - assert {:layer 1} {:layer 2} (forall ii:int :: 0 <= ii && ii < max && old_valid[ii] ==> valid[ii] && old_elt[ii] == elt[ii]); + yield; + assert {:layer 1} {:layer 2} (forall ii:int :: 0 <= ii && ii < max && old_valid[ii] ==> valid[ii] && old_elt[ii] == elt[ii]); } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/multiset.bpl.expect boogie-2.4.1+dfsg/Test/civl/multiset.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/multiset.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/multiset.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 85 verified, 0 errors +Boogie program verifier finished with 132 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/new1.bpl boogie-2.4.1+dfsg/Test/civl/new1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/new1.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/new1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -35,8 +35,14 @@ yield; } -procedure {:yields} {:layer 0,1} SetG(val:int); -ensures {:atomic} |{A: g := val; return true; }|; +procedure {:atomic} {:layer 1} AtomicSetG(val:int) +modifies g; +{ g := val; } -procedure {:yields} {:layer 0,1} IncrG(); -ensures {:atomic} |{A: g := g + 1; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicSetG"} SetG(val:int); + +procedure {:atomic} {:layer 1} AtomicIncrG() +modifies g; +{ g := g + 1; } + +procedure {:yields} {:layer 0} {:refines "AtomicIncrG"} IncrG(); \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/nocollector.bpl.expect boogie-2.4.1+dfsg/Test/civl/nocollector.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/nocollector.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/nocollector.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ - -Boogie program verifier finished with 1 verified, 0 errors +nocollector.bpl(3,18): Error: Missing collector for linear variable x +1 type checking errors detected in nocollector.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ogcounter2.bpl boogie-2.4.1+dfsg/Test/civl/ogcounter2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ogcounter2.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/ogcounter2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,92 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type X; +const unique MainTid: X; +const unique Nil: X; + +var {:layer 2,5} x: int; +var {:layer 2,3} lock: X; + +var {:layer 1,4}{:linear "tid"} unallocated:[X]bool; + +procedure {:right} {:layer 2,4} AtomicAllocTid() returns ({:linear "tid"} tid: X) +modifies unallocated; +{ assume tid != Nil && unallocated[tid]; unallocated[tid] := false; } + +procedure {:yields} {:layer 1} {:refines "AtomicAllocTid"} AllocTid() returns ({:linear "tid"} tid: X); + +procedure {:right} {:layer 3} atomic_acq({:linear "tid"} tid: X) +modifies lock; +{ assert tid != Nil; assume lock == Nil; lock := tid; } + +procedure {:yields} {:layer 2} {:refines "atomic_acq"} acq({:linear "tid"} tid: X); + +procedure {:left} {:layer 3} atomic_rel({:linear "tid"} tid: X) +modifies lock; +{ assert tid != Nil && lock == tid; lock := Nil; } + +procedure {:yields} {:layer 2} {:refines "atomic_rel"} rel({:linear "tid"} tid: X); + +procedure {:both} {:layer 3} atomic_read({:linear "tid"} tid: X) returns (v: int) +{ assert tid != Nil && lock == tid; v := x; } + +procedure {:yields} {:layer 2} {:refines "atomic_read"} read({:linear "tid"} tid: X) returns (v: int); + +procedure {:both} {:layer 3} atomic_write({:linear "tid"} tid: X, v: int) +modifies x; +{ assert tid != Nil && lock == tid; x := v; } + +procedure {:yields} {:layer 2} {:refines "atomic_write"} write({:linear "tid"} tid: X, v: int); + +procedure {:left} {:layer 4} AtomicIncr({:linear "tid"} tid: X) +modifies x; +{ x := x + 1; } + +procedure {:yields} {:layer 3} {:refines "AtomicIncr"} Incr({:linear "tid"} tid: X) +requires {:layer 3} tid != Nil; +{ + var t: int; + + yield; + call acq(tid); + call t := read(tid); + call write(tid, t+1); + call rel(tid); + yield; +} + +procedure {:left} {:layer 5} AtomicIncrBy2() +modifies x; +{ x := x + 2; } + +procedure {:yields} {:layer 4} {:refines "AtomicIncrBy2"} IncrBy2() +{ + var {:linear "tid"} tid1: X; + var {:linear "tid"} tid2: X; + yield; + call tid1 := AllocTid(); + call tid2 := AllocTid(); + par Incr(tid1) | Incr(tid2); + yield; +} + +procedure {:yields} {:layer 5} EqualTo2({:linear "tid"} tid: X) +requires {:layer 5} tid == MainTid && x == 0; +{ + yield; + assert {:layer 5} tid == MainTid && x == 0; + call IncrBy2(); + yield; + assert {:layer 5} x == 2; +} + +function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; +function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool +{ + MapConstBool(false)[x := true] +} +function {:inline} {:linear "tid"} TidSetCollector(x: [X]bool) : [X]bool +{ + x +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ogcounter2.bpl.expect boogie-2.4.1+dfsg/Test/civl/ogcounter2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ogcounter2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/ogcounter2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 39 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ogcounter.bpl boogie-2.4.1+dfsg/Test/civl/ogcounter.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ogcounter.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/ogcounter.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,41 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type X; +const MainTid: X; + +var {:layer 0,2} x: int; + +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(); + +procedure {:left} {:layer 1} AtomicIncr() +modifies x; +{ x := x + 1; } + +procedure {:yields} {:layer 1} {:refines "AtomicIncrBy2"} IncrBy2() +{ + yield; + par Incr() | Incr(); + yield; +} + +procedure {:left} {:layer 2} AtomicIncrBy2() +modifies x; +{ x := x + 2; } + +procedure {:yields} {:layer 2} EqualTo2({:linear "tid"} tid: X) +requires {:layer 2} tid == MainTid && x == 0; +{ + yield; + assert {:layer 2} tid == MainTid && x == 0; + call IncrBy2(); + yield; + assert {:layer 2} x == 2; +} + +function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; +function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool +{ + MapConstBool(false)[x := true] +} + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ogcounter.bpl.expect boogie-2.4.1+dfsg/Test/civl/ogcounter.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ogcounter.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/ogcounter.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 5 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/one.bpl boogie-2.4.1+dfsg/Test/civl/one.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/one.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/one.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -2,11 +2,11 @@ // RUN: %diff "%s.expect" "%t" var {:layer 0,1} x:int; -procedure {:yields} {:layer 0,1} Set(v: int); -ensures {:atomic} -|{A: - x := v; return true; -}|; +procedure {:atomic} {:layer 1} AtomicSet(v: int) +modifies x; +{ x := v; } + +procedure {:yields} {:layer 0} {:refines "AtomicSet"} Set(v: int); procedure {:yields} {:layer 1} B() { diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel1.bpl boogie-2.4.1+dfsg/Test/civl/parallel1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel1.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/parallel1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -9,17 +9,17 @@ yield; } -procedure {:yields} {:layer 0,1} Incr(); -ensures {:atomic} -|{A: - g := g + 1; return true; -}|; +procedure {:atomic} {:layer 1} AtomicIncr() +modifies g; +{ g := g + 1; } -procedure {:yields} {:layer 0,1} Set(v: int); -ensures {:atomic} -|{A: - g := v; return true; -}|; +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(); + +procedure {:atomic} {:layer 1} AtomicSet(v: int) +modifies g; +{ g := v; } + +procedure {:yields} {:layer 0} {:refines "AtomicSet"} Set(v: int); procedure {:yields} {:layer 1} PC() ensures {:layer 1} g == 3; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel1.bpl.expect boogie-2.4.1+dfsg/Test/civl/parallel1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel1.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/parallel1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,8 +1,9 @@ parallel1.bpl(30,3): Error: Non-interference check failed Execution trace: parallel1.bpl(7,3): anon0 - (0,0): anon00 - parallel1.bpl(14,3): inline$Incr_1$0$A + parallel1.bpl(7,3): anon0_1 + parallel1.bpl(14,5): inline$AtomicIncr_1$0$anon0 + (0,0): YieldChecker (0,0): inline$Impl_YieldChecker_PC_1$0$L0 Boogie program verifier finished with 7 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel2.bpl boogie-2.4.1+dfsg/Test/civl/parallel2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel2.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/parallel2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -15,10 +15,13 @@ yield; } -procedure {:yields} {:layer 0,1} Write(idx: int, val: int); -ensures {:atomic} |{A: a[idx] := val; return true; }|; +procedure {:atomic} {:layer 1} AtomicWrite(idx: int, val: int) +modifies a; +{ a[idx] := val; } -procedure {:yields} {:layer 1} main() +procedure {:yields} {:layer 0} {:refines "AtomicWrite"} Write(idx: int, val: int); + +procedure {:yields} {:layer 1} main() { var {:linear "tid"} i: int; var {:linear "tid"} j: int; @@ -38,7 +41,7 @@ assert {:layer 1} a[i] == 42; } -procedure {:yields} {:layer 1} u({:linear_in "tid"} i': int) returns ({:linear "tid"} i: int) +procedure {:yields} {:layer 1} u({:linear_in "tid"} i': int) returns ({:linear "tid"} i: int) { i := i'; @@ -55,5 +58,5 @@ assert {:layer 1} old(a)[i] == a[i]; } -procedure {:yields} {:layer 0,1} AllocateLow() returns ({:linear "tid"} tid: int); -ensures {:atomic} |{ A: return true; }|; +procedure {:yields} {:layer 0} AllocateLow() returns ({:linear "tid"} tid: int); + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel4.bpl boogie-2.4.1+dfsg/Test/civl/parallel4.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel4.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/parallel4.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -15,7 +15,7 @@ MapConstBool(false)[x := true] } -procedure {:yields} {:layer 1} main() +procedure {:yields} {:layer 1} main() { var {:linear "tid"} i: int; var {:linear "tid"} j: int; @@ -33,13 +33,16 @@ yield; } -procedure {:yields} {:layer 0,1} Incr(); -ensures {:atomic} |{A: a := a + 1; return true; }|; +procedure {:atomic} {:layer 1} AtomicIncr() +modifies a; +{ a := a + 1; } + +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(); procedure {:yields} {:layer 1} Yield() { yield; } -procedure {:yields} {:layer 0,1} AllocateLow() returns ({:linear "tid"} tid: int); -ensures {:atomic} |{ A: return true; }|; +procedure {:yields} {:layer 0} AllocateLow() returns ({:linear "tid"} tid: int); + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel4.bpl.expect boogie-2.4.1+dfsg/Test/civl/parallel4.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel4.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/parallel4.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,6 +1,6 @@ parallel4.bpl(31,3): Error BP5001: This assertion might not hold. Execution trace: parallel4.bpl(29,5): anon0 - (0,0): anon01 + parallel4.bpl(29,5): anon0_1 Boogie program verifier finished with 7 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel5.bpl boogie-2.4.1+dfsg/Test/civl/parallel5.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/parallel5.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/parallel5.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -15,10 +15,13 @@ MapConstBool(false)[x := true] } -procedure {:yields} {:layer 0,1} Write(idx: int, val: int); -ensures {:atomic} |{A: a[idx] := val; return true; }|; +procedure {:atomic} {:layer 1} AtomicWrite(idx: int, val: int) +modifies a; +{ a[idx] := val; } -procedure {:yields} {:layer 1} main() +procedure {:yields} {:layer 0} {:refines "AtomicWrite"} Write(idx: int, val: int); + +procedure {:yields} {:layer 1} main() { var {:linear "tid"} i: int; var {:linear "tid"} j: int; @@ -38,7 +41,7 @@ assert {:layer 1} a[i] == 42; } -procedure {:yields} {:layer 1} u({:linear_in "tid"} i': int) returns ({:linear "tid"} i: int) +procedure {:yields} {:layer 1} u({:linear_in "tid"} i': int) returns ({:linear "tid"} i: int) { i := i'; @@ -55,5 +58,4 @@ assert {:layer 1} old(a)[i] == a[i]; } -procedure {:yields} {:layer 0,1} AllocateLow() returns ({:linear "tid"} tid: int); -ensures {:atomic} |{ A: return true; }|; +procedure {:yields} {:layer 0} AllocateLow() returns ({:linear "tid"} tid: int); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/par-incr.bpl boogie-2.4.1+dfsg/Test/civl/par-incr.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/par-incr.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/par-incr.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -1,13 +1,19 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -var {:layer 0} x: int; +var {:layer 0,3} x: int; -procedure {:yields} {:layer 0,1} Incr(); -ensures {:right} |{ A: x := x + 1; return true; }|; +procedure {:right} {:layer 1} AtomicIncr() +modifies x; +{ x := x + 1; } -procedure {:yields} {:layer 1,2} Incr2() -ensures {:right} |{ A: x := x + 2; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(); + +procedure {:right} {:layer 2} AtomicIncr2() +modifies x; +{ x := x + 2; } + +procedure {:yields} {:layer 1} {:refines "AtomicIncr2"} Incr2() { yield; par Incr() | Incr(); @@ -19,8 +25,11 @@ yield; } -procedure {:yields} {:layer 2,3} Incr4() -ensures {:atomic} |{ A: x := x + 4; return true; }|; +procedure {:atomic} {:layer 3} AtomicIncr4() +modifies x; +{ x := x + 4; } + +procedure {:yields} {:layer 2} {:refines "AtomicIncr4"} Incr4() { yield; par Incr2() | Incr2() | Yield(); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/par-intro-bug.bpl boogie-2.4.1+dfsg/Test/civl/par-intro-bug.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/par-intro-bug.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/par-intro-bug.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,24 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 0} x : int; + +procedure {:layer 0} set_x(v: int) +ensures {:layer 0} x == v; +modifies x; +{ + x := v; +} + +procedure {:yields} {:layer 0} yield_set_x(v: int) +ensures {:layer 0} x == v; +{ + yield; + call set_x(v); +} + +procedure {:yields} {:layer 0} main() +{ + par yield_set_x(4) | yield_set_x(3); + assert {:layer 0} false; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/par-intro-bug.bpl.expect boogie-2.4.1+dfsg/Test/civl/par-intro-bug.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/par-intro-bug.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/par-intro-bug.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ +par-intro-bug.bpl(13,31): Error: Implementation yield_set_x fails bracket check at layer 0. All code that accesses global variables must be bracketed by yields. +1 type checking errors detected in par-intro-bug.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/perm.bpl boogie-2.4.1+dfsg/Test/civl/perm.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/perm.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/perm.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -26,14 +26,14 @@ yield; } -procedure {:yields} {:layer 1} foo({:linear_in "Perm"} permVar_in: [int]bool) +procedure {:yields} {:layer 1} foo({:linear_in "Perm"} permVar_in: [int]bool) requires {:layer 1} permVar_in != ch_mapconstbool(false); requires {:layer 1} permVar_in[1]; requires {:layer 1} x == 0; { var {:linear "Perm"} permVar_out: [int]bool; permVar_out := permVar_in; - + yield; assert {:layer 1} permVar_out[1]; assert {:layer 1} x == 0; @@ -45,5 +45,8 @@ assert {:layer 1} x == 1; } -procedure {:yields} {:layer 0,1} Incr(); -ensures {:atomic} |{A: x := x + 1; return true; }|; \ No newline at end of file +procedure {:atomic} {:layer 1} AtomicIncr() +modifies x; +{ x := x + 1; } + +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(); \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/t-s.bpl boogie-2.4.1+dfsg/Test/civl/podelski/t-s.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/t-s.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/podelski/t-s.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,51 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 0,1} s:int; +var {:layer 0,1} t:int; + +function {:inline} Inv (s:int, t:int) : bool +{ + t == s +// t >= s +} + +procedure {:yields}{:layer 2} main () +requires {:layer 1} s == t; +{ + yield; assert {:layer 1} Inv(s, t); + while (*) + invariant {:layer 1} Inv(s, t); + { + async call incdec(); + yield; assert {:layer 1} Inv(s, t); + } + yield; +} + +procedure {:yields}{:layer 1} incdec() +requires {:layer 1} Inv(s, t); +ensures {:layer 1} Inv(s, t); +{ + yield; assert {:layer 1} Inv(s, t); + call inc_t(); + call inc_s(); + yield; assert {:layer 1} Inv(s, t); +} + +procedure {:right}{:layer 1} INC_T () +modifies t; +{ + assert s <= t; + t := t + 1; +} + +procedure {:atomic}{:layer 1} INC_S () +modifies s; +{ + assert s < t; + s := s + 1; +} + +procedure {:yields}{:layer 0}{:refines "INC_T"} inc_t (); +procedure {:yields}{:layer 0}{:refines "INC_S"} inc_s (); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/t-s.bpl.expect boogie-2.4.1+dfsg/Test/civl/podelski/t-s.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/t-s.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/podelski/t-s.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 7 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/x.bpl boogie-2.4.1+dfsg/Test/civl/podelski/x.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/x.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/podelski/x.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,49 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 0,1} x:int; + +function {:inline} Inv (x:int) : bool +{ + x >= 0 +} + +procedure {:yields}{:layer 2} main () +requires {:layer 1} Inv(x); +{ + yield; assert {:layer 1} Inv(x); + while (*) + invariant {:layer 1} Inv(x); + { + async call incdec(); + yield; assert {:layer 1} Inv(x); + } + yield; +} + +procedure {:yields}{:layer 1} incdec() +requires {:layer 1} Inv(x); +ensures {:layer 1} Inv(x); +{ + yield; assert {:layer 1} Inv(x); + call geq0_inc(); + call geq0_dec(); + yield; assert {:layer 1} Inv(x); +} + +procedure {:right}{:layer 1} GEQ0_INC () +modifies x; +{ + assert x >= 0; + x := x + 1; +} + +procedure {:atomic}{:layer 1} GEQ0_DEC () +modifies x; +{ + assert x >= 0; + x := x - 1; +} + +procedure {:yields}{:layer 0}{:refines "GEQ0_INC"} geq0_inc (); +procedure {:yields}{:layer 0}{:refines "GEQ0_DEC"} geq0_dec (); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/x.bpl.expect boogie-2.4.1+dfsg/Test/civl/podelski/x.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/x.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/podelski/x.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 8 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/x_perm.bpl boogie-2.4.1+dfsg/Test/civl/podelski/x_perm.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/x_perm.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/podelski/x_perm.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,136 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// Permission types +type {:datatype} AB; +function {:constructor} AB (x:int) : AB; +type {:datatype} A; +function {:constructor} A (x:int) : A; +type {:datatype} B; +function {:constructor} B (x:int) : B; + +// Linear collectors +function {:builtin "MapConst"} MapConstBool(bool): [int]bool; + +function {:inline} {:linear "perm"} ABCollector(ab: AB) : [int]bool +{ MapConstBool(false)[x#AB(ab) := true][-x#AB(ab) := true] } +function {:inline} {:linear "perm"} ABSetCollector(abs: [AB]bool) : [int]bool +{ (lambda i:int :: (exists ab:AB :: abs[ab] && (i == x#AB(ab) || i == -x#AB(ab)))) } + +function {:inline} {:linear "perm"} ACollector(a: A) : [int]bool +{ MapConstBool(false)[x#A(a) := true] } +function {:inline} {:linear "perm"} ASetCollector(as: [A]bool) : [int]bool +{ (lambda i:int :: (exists a:A :: as[a] && (i == x#A(a)))) } + +function {:inline} {:linear "perm"} BCollector(b: B) : [int]bool +{ MapConstBool(false)[-x#B(b) := true] } +function {:inline} {:linear "perm"} BSetCollector(bs: [B]bool) : [int]bool +{ (lambda i:int :: (exists b:B :: bs[b] && (i == -x#B(b)))) } + +function {:inline} bToA (b:B) : A { A(x#B(b)) } + +// Global variables +var {:layer 0,1} x:int; +var {:layer 0,1}{:linear "perm"} As:[A]bool; +var {:layer 0,1}{:linear "perm"} Bs:[B]bool; + +// Invariant +function {:inline} Inv (x:int, As:[A]bool, Bs: [B]bool) : bool +{ + x >= cardAs(As) - cardBs(Bs) + && (forall b:B :: Bs[b] ==> As[bToA(b)]) +} + +// Definitions and facts about cardinality +function {:inline} cardAs (As:[A]bool) : int; +axiom (forall As:[A]bool :: cardAs(As) >= 0); + +function {:inline} cardBs (Bs:[B]bool) : int; +axiom (forall Bs:[B]bool :: cardBs(Bs) >= 0); + +procedure {:layer 1} Lemma_add_to_A (a:A); +requires {:layer 1} !As[a]; +ensures {:layer 1} cardAs(As[a := true]) == cardAs(As) + 1; + +procedure {:layer 1} Lemma_add_to_B (b:B); +requires {:layer 1} !Bs[b]; +ensures {:layer 1} cardBs(Bs[b := true]) == cardBs(Bs) + 1; + +procedure {:layer 1} Lemma_card_geq (); +requires {:layer 1} (forall b:B :: Bs[b] ==> As[bToA(b)]); +ensures {:layer 1} cardAs(As) >= cardBs(Bs); + +procedure {:layer 1} Lemma_card_greater (_b:B); +requires {:layer 1} (forall b:B :: Bs[b] ==> As[bToA(b)]); +requires {:layer 1} !Bs[_b]; +requires {:layer 1} As[bToA(_b)]; +ensures {:layer 1} cardAs(As) > cardBs(Bs); + +// Acutal program +procedure {:yields}{:layer 2} main () +requires {:layer 1} Inv(x, As, Bs); +{ + var i:int; + var {:linear "perm"} ab:AB; + yield; assert {:layer 1} Inv(x, As, Bs); + while (*) + invariant {:layer 1} Inv(x, As, Bs); + { + call ab := alloc_ab(); + async call incdec(ab); + yield; assert {:layer 1} Inv(x, As, Bs); + } + yield; +} + +procedure {:yields}{:layer 1} alloc_ab () returns ({:linear "perm"} ab:AB); +ensures {:layer 1} As == old(As); +ensures {:layer 1} Bs == old(Bs); +ensures {:layer 1} x == old(x); + +procedure {:yields}{:layer 1} split_ab ({:linear_in "perm"} ab:AB) returns ({:linear "perm"} a:A, {:linear "perm"} b:B); +ensures {:layer 1} x#A(a) == x#AB(ab); +ensures {:layer 1} x#B(b) == x#AB(ab); +ensures {:layer 1} As == old(As); +ensures {:layer 1} Bs == old(Bs); +ensures {:layer 1} x == old(x); + +procedure {:yields}{:layer 1} incdec({:linear_in "perm"} ab:AB) +requires {:layer 1} Inv(x, As, Bs); +ensures {:layer 1} Inv(x, As, Bs); +{ + var {:linear "perm"} a:A; + var {:linear "perm"} b:B; + yield; assert {:layer 1} Inv(x, As, Bs); + call a,b := split_ab(ab); + call Lemma_card_geq(); + call Lemma_add_to_A(a); + call geq0_inc(a, b); + yield; + assert {:layer 1} Inv(x, As, Bs) && As[bToA(b)]; + call Lemma_card_greater(b); + call Lemma_add_to_B(b); + call geq0_dec(b); + yield; assert {:layer 1}{:expand} Inv(x, As, Bs); +} + +procedure {:atomic}{:layer 1} GEQ0_INC ({:linear_in "perm"} a:A, {:linear "perm"} b:B) +modifies x, As; +{ + assert x >= 0; + assert !As[a]; + x := x + 1; + As[a] := true; +} + +procedure {:atomic}{:layer 1} GEQ0_DEC ({:linear_in "perm"} b:B) +modifies x, Bs; +{ + assert x >= 0; + assert !Bs[b]; + x := x - 1; + Bs[b] := true; +} + +procedure {:yields}{:layer 0}{:refines "GEQ0_INC"} geq0_inc ({:linear_in "perm"} a:A, {:linear "perm"} b:B); +procedure {:yields}{:layer 0}{:refines "GEQ0_DEC"} geq0_dec ({:linear_in "perm"} b:B); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/x_perm.bpl.expect boogie-2.4.1+dfsg/Test/civl/podelski/x_perm.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/podelski/x_perm.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/podelski/x_perm.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 7 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program1.bpl boogie-2.4.1+dfsg/Test/civl/Program1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program1.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/Program1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -2,32 +2,34 @@ // RUN: %diff "%s.expect" "%t" var {:layer 0,1} x:int; -procedure {:yields} {:layer 1} p() -requires {:layer 1} x >= 5; -ensures {:layer 1} x >= 8; -{ - yield; - assert {:layer 1} x >= 5; - call Incr(1); - yield; - assert {:layer 1} x >= 6; - call Incr(1); - yield; - assert {:layer 1} x >= 7; - call Incr(1); - yield; - assert {:layer 1} x >= 8; +procedure {:yields} {:layer 1} p() +requires {:layer 1} x >= 5; +ensures {:layer 1} x >= 8; +{ + yield; + assert {:layer 1} x >= 5; + call Incr(1); + yield; + assert {:layer 1} x >= 6; + call Incr(1); + yield; + assert {:layer 1} x >= 7; + call Incr(1); + yield; + assert {:layer 1} x >= 8; } -procedure {:yields} {:layer 1} q() -{ - yield; - call Incr(3); - yield; +procedure {:yields} {:layer 1} q() +{ + yield; + call Incr(3); + yield; } -procedure {:yields} {:layer 0,1} Incr(val: int); -ensures {:atomic} -|{A: - x := x + val; return true; -}|; +procedure {:atomic} {:layer 1,1} AtomicIncr(val: int) +modifies x; +{ + x := x + val; +} + +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(val: int); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program2.bpl boogie-2.4.1+dfsg/Test/civl/Program2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program2.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/Program2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -3,35 +3,37 @@ var {:layer 0,1} x:int; procedure {:yields} {:layer 1} yield_x(n: int) -requires {:layer 1} x >= n; -ensures {:layer 1} x >= n; +requires {:layer 1} x >= n; +ensures {:layer 1} x >= n; { - yield; - assert {:layer 1} x >= n; + yield; + assert {:layer 1} x >= n; } -procedure {:yields} {:layer 1} p() -requires {:layer 1} x >= 5; -ensures {:layer 1} x >= 8; -{ - call yield_x(5); - call Incr(1); - call yield_x(6); - call Incr(1); - call yield_x(7); - call Incr(1); - call yield_x(8); +procedure {:yields} {:layer 1} p() +requires {:layer 1} x >= 5; +ensures {:layer 1} x >= 8; +{ + call yield_x(5); + call Incr(1); + call yield_x(6); + call Incr(1); + call yield_x(7); + call Incr(1); + call yield_x(8); } -procedure {:yields} {:layer 1} q() -{ - yield; - call Incr(3); - yield; +procedure {:yields} {:layer 1} q() +{ + yield; + call Incr(3); + yield; +} + +procedure {:atomic} {:layer 1,1} AtomicIncr(val: int) +modifies x; +{ + x := x + val; } -procedure {:yields} {:layer 0,1} Incr(val: int); -ensures {:atomic} -|{A: - x := x + val; return true; -}|; +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(val: int); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program3.bpl boogie-2.4.1+dfsg/Test/civl/Program3.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program3.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/Program3.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -3,34 +3,36 @@ var {:layer 0,1} x:int; procedure {:yields} {:layer 1} yield_x() -ensures {:layer 1} x >= old(x); +ensures {:layer 1} x >= old(x); { - yield; - assert {:layer 1} x >= old(x); + yield; + assert {:layer 1} x >= old(x); } -procedure {:yields} {:layer 1} p() -requires {:layer 1} x >= 5; -ensures {:layer 1} x >= 8; -{ - call yield_x(); - call Incr(1); - call yield_x(); - call Incr(1); - call yield_x(); - call Incr(1); - call yield_x(); +procedure {:yields} {:layer 1} p() +requires {:layer 1} x >= 5; +ensures {:layer 1} x >= 8; +{ + call yield_x(); + call Incr(1); + call yield_x(); + call Incr(1); + call yield_x(); + call Incr(1); + call yield_x(); } -procedure {:yields} {:layer 1} q() -{ - yield; - call Incr(3); - yield; +procedure {:yields} {:layer 1} q() +{ + yield; + call Incr(3); + yield; +} + +procedure {:atomic} {:layer 1,1} AtomicIncr(val: int) +modifies x; +{ + x := x + val; } -procedure {:yields} {:layer 0,1} Incr(val: int); -ensures {:atomic} -|{A: - x := x + val; return true; -}|; +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(val: int); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program4.bpl boogie-2.4.1+dfsg/Test/civl/Program4.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program4.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/Program4.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -1,139 +1,36 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -var {:layer 0,2} a:[int]int; -var {:layer 0,1} count: int; -var {:layer 1,1} {:linear "tid"} allocated:[int]bool; +var {:layer 0,1} x:int; -procedure {:yields} {:layer 2} main() -requires {:layer 1} allocated == MapConstBool(false); +procedure {:yields} {:layer 1} yield_x() +ensures {:layer 1} x >= old(x); { - var {:layer 1} {:linear "tid"} tid:int; - var i: int; - - yield; - assert {:layer 1} AllocInv(count, allocated); - while (true) - invariant {:layer 1} AllocInv(count, allocated); - { - call tid, i := Allocate(); - async call P(tid, i); - yield; - assert {:layer 1} AllocInv(count, allocated); - } - yield; + yield; + assert {:layer 1} x >= old(x); } -procedure {:yields} {:layer 2} P({:layer 1} {:linear "tid"} tid: int, i: int) -requires {:layer 1} tid == i; -requires {:layer 1} AllocInv(count, allocated); -ensures {:layer 1} AllocInv(count, allocated); -ensures {:layer 2} a[tid] == old(a)[tid] + 1; -{ - var t:int; - - yield; - assert {:layer 1} AllocInv(count, allocated); - assert {:layer 2} a[tid] == old(a)[tid]; - call t := Read(tid, i); - yield; - assert {:layer 1} AllocInv(count, allocated); - assert {:layer 2} a[tid] == t; - call Write(tid, i, t + 1); - yield; - assert {:layer 1} AllocInv(count, allocated); - assert {:layer 2} a[tid] == t + 1; -} - -procedure {:yields} {:layer 1,2} Allocate() returns ({:layer 1} {:linear "tid"} tid: int, i: int) -requires {:layer 1} AllocInv(count, allocated); -ensures {:layer 1} AllocInv(count, allocated); -ensures {:layer 1} tid == i; -ensures {:atomic} -|{A: - return true; -}|; +procedure {:yields} {:layer 1} p() +requires {:layer 1} x >= 5; +ensures {:layer 1} x >= 8; { - yield; - assert {:layer 1} AllocInv(count, allocated); - call i := AllocateLow(); - call tid := MakeLinear(i); - yield; - assert {:layer 1} AllocInv(count, allocated); + call yield_x(); + call Incr(1); + call Incr(1); + call Incr(1); + call yield_x(); } -procedure {:yields} {:layer 1,2} Read({:layer 1} {:linear "tid"} tid: int, i: int) returns (val: int) -requires {:layer 1} tid == i; -requires {:layer 1} AllocInv(count, allocated); -ensures {:layer 1} AllocInv(count, allocated); -ensures {:atomic} -|{A: - val := a[tid]; return true; -}|; +procedure {:yields} {:layer 1} q() { - yield; - assert {:layer 1} AllocInv(count, allocated); - call val := ReadLow(i); - yield; - assert {:layer 1} AllocInv(count, allocated); + yield; + call Incr(3); + yield; } -procedure {:yields} {:layer 1,2} Write({:layer 1} {:linear "tid"} tid: int, i: int, val: int) -requires {:layer 1} tid == i; -requires {:layer 1} AllocInv(count, allocated); -ensures {:layer 1} AllocInv(count, allocated); -ensures {:atomic} -|{A: - a[tid] := val; return true; -}|; -{ - yield; - assert {:layer 1} AllocInv(count, allocated); - call WriteLow(i, val); - yield; - assert {:layer 1} AllocInv(count, allocated); -} - -function {:inline} AllocInv(count: int, allocated:[int]bool): (bool) -{ - (forall x: int :: allocated[x] ==> x < count) -} - -procedure {:yields} {:layer 0,1} ReadLow(i: int) returns (val: int); -ensures {:atomic} -|{A: - val := a[i]; return true; -}|; - -procedure {:yields} {:layer 0,1} WriteLow(i: int, val: int); -ensures {:atomic} -|{A: - a[i] := val; return true; -}|; - -procedure {:yields} {:layer 0,1} AllocateLow() returns (i: int); -ensures {:atomic} -|{A: - i := count; - count := i + 1; - return true; -}|; - -// We can prove that this primitive procedure preserves the permission invariant locally. -// We only need to using its specification and the definitions of TidCollector and TidSetCollector. -procedure {:layer 1} MakeLinear(i: int) returns ({:linear "tid"} tid: int); -requires !allocated[i]; -modifies allocated; -ensures tid == i && allocated == old(allocated)[i := true]; - -function {:builtin "MapConst"} MapConstBool(bool): [int]bool; -function {:builtin "MapOr"} MapOr([int]bool, [int]bool) : [int]bool; - -function {:inline} {:linear "tid"} TidCollector(x: int) : [int]bool -{ - MapConstBool(false)[x := true] -} -function {:inline} {:linear "tid"} TidSetCollector(x: [int]bool) : [int]bool +procedure {:both} {:layer 1,1} AtomicIncr(val: int) +modifies x; { - x + x := x + val; } +procedure {:yields} {:layer 0} {:refines "AtomicIncr"} Incr(val: int); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program4.bpl.expect boogie-2.4.1+dfsg/Test/civl/Program4.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program4.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/Program4.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 12 verified, 0 errors +Boogie program verifier finished with 7 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program5.bpl boogie-2.4.1+dfsg/Test/civl/Program5.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program5.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/Program5.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -type Tid; -const unique nil: Tid; - -function {:inline} UNALLOC():int { 0 } -function {:inline} WHITE():int { 1 } -function {:inline} GRAY():int { 2 } -function {:inline} BLACK():int { 3 } -function {:inline} Unalloc(i:int) returns(bool) { i <= 0 } -function {:inline} White(i:int) returns(bool) { i == 1 } -function {:inline} WhiteOrLighter(i:int) returns(bool) { i <= 1 } -function {:inline} Gray(i:int) returns(bool) { i == 2 } -function {:inline} Black(i:int) returns(bool) { i >= 3 } - -procedure {:yields} {:layer 2} YieldColorOnlyGetsDarker() -ensures {:layer 2} Color >= old(Color); -{ - yield; - assert {:layer 2} Color >= old(Color); -} - -procedure {:yields} {:layer 2,3} WriteBarrier({:linear "tid"} tid:Tid) -ensures {:atomic} |{ A: assert tid != nil; goto B, C; - B: assume White(Color); Color := GRAY(); return true; - C: assume !White(Color); return true;}|; -requires {:layer 2} Color >= WHITE(); -ensures {:layer 2} Color >= GRAY(); -{ - var colorLocal:int; - yield; - assert {:layer 2} Color >= WHITE(); - call colorLocal := GetColorNoLock(); - call YieldColorOnlyGetsDarker(); - if (WhiteOrLighter(colorLocal)) { call WriteBarrierSlow(tid); } - yield; - assert {:layer 2} Color >= GRAY(); -} - -procedure {:yields} {:layer 1,2} WriteBarrierSlow({:linear "tid"} tid:Tid) -ensures {:atomic} |{ A: assert tid != nil; goto B, C; - B: assume WhiteOrLighter(Color); Color := GRAY(); return true; - C: assume !WhiteOrLighter(Color); return true; }|; -{ - var colorLocal:int; - yield; - call AcquireLock(tid); - call colorLocal := GetColorLocked(tid); - if (WhiteOrLighter(colorLocal)) { call SetColorLocked(tid, GRAY()); } - call ReleaseLock(tid); - yield; -} - -procedure {:yields} {:layer 0,1} AcquireLock({:linear "tid"} tid: Tid); - ensures {:right} |{ A: assert tid != nil; assume lock == nil; lock := tid; return true; }|; - -procedure {:yields} {:layer 0,1} ReleaseLock({:linear "tid"} tid: Tid); - ensures {:left} |{ A: assert tid != nil; assert lock == tid; lock := nil; return true; }|; - -procedure {:yields} {:layer 0,1} SetColorLocked({:linear "tid"} tid:Tid, newCol:int); - ensures {:atomic} |{A: assert tid != nil; assert lock == tid; Color := newCol; return true;}|; - -procedure {:yields} {:layer 0,1} GetColorLocked({:linear "tid"} tid:Tid) returns (col:int); - ensures {:both} |{A: assert tid != nil; assert lock == tid; col := Color; return true;}|; - -procedure {:yields} {:layer 0,2} GetColorNoLock() returns (col:int); - ensures {:atomic} |{A: col := Color; return true;}|; - - - -function {:builtin "MapConst"} MapConstBool(bool): [Tid]bool; -function {:builtin "MapOr"} MapOr([Tid]bool, [Tid]bool) : [Tid]bool; - -var {:layer 0} Color:int; -var {:layer 0} lock:Tid; - -function {:inline} {:linear "tid"} TidCollector(x: Tid) : [Tid]bool -{ - MapConstBool(false)[x := true] -} -function {:inline} {:linear "tid"} TidSetCollector(x: [Tid]bool) : [Tid]bool -{ - x -} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program5.bpl.expect boogie-2.4.1+dfsg/Test/civl/Program5.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Program5.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/Program5.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 21 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/purity-issta.bpl boogie-2.4.1+dfsg/Test/civl/purity-issta.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/purity-issta.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/purity-issta.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,87 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +type X; +const nil: X; + +function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; +function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool +{ + MapConstBool(false)[x := true] +} + +const max: int; +axiom 0 <= max; + +var {:layer 0,1} l: [int]X; +var {:layer 0,2} status: [int]bool; + +procedure {:atomic} {:layer 2} atomic_Alloc({:linear "tid"} tid: X) returns (r: int) +modifies status; +{ + assert tid != nil; + if (*) { + assume r == -1; + } else { + assume 0 <= r && r < max && status[r]; + status[r] := false; + } +} + +procedure {:atomic} {:layer 2} atomic_Free({:linear "tid"} tid: X, i: int) +modifies status; +{ assert tid != nil; status[i] := true; } + +procedure {:yields} {:layer 1} {:refines "atomic_Alloc"} Alloc({:linear "tid"} tid: X) returns (r: int) +{ + var i: int; + var b: bool; + + i := 0; + r := -1; + while (i < max) + invariant {:layer 1} 0 <= i; + { + yield; + call acquire(tid, i); + call b := Read(tid, i); + if (b) { + call Write(tid, i, false); + call release(tid, i); + r := i; + break; + } + call release(tid, i); + i := i + 1; + yield; + } + yield; +} + +procedure {:yields} {:layer 1} {:refines "atomic_Free"} Free({:linear "tid"} tid: X, i: int) +{ + yield; + call acquire(tid, i); + call Write(tid, i, true); + call release(tid, i); + yield; +} + +procedure {:right} {:layer 1} atomic_acquire({:linear "tid"} tid: X, i: int) +modifies l; +{ assert tid != nil; assume l[i] == nil; l[i] := tid; } + +procedure {:left} {:layer 1} atomic_release({:linear "tid"} tid: X, i: int) +modifies l; +{ assert tid != nil; assert l[i] == tid; l[i] := nil; } + +procedure {:both} {:layer 1} atomic_Read({:linear "tid"} tid: X, i: int) returns (val: bool) +{ assert tid != nil; assert l[i] == tid; val := status[i]; } + +procedure {:both} {:layer 1} atomic_Write({:linear "tid"} tid: X, i: int, val: bool) +modifies status; +{ assert tid != nil; assert l[i] == tid; status[i] := val; } + +procedure {:yields} {:layer 0} {:refines "atomic_acquire"} acquire({:linear "tid"} tid: X, i: int); +procedure {:yields} {:layer 0} {:refines "atomic_release"} release({:linear "tid"} tid: X, i: int); +procedure {:yields} {:layer 0} {:refines "atomic_Read"} Read({:linear "tid"} tid: X, i: int) returns (val: bool); +procedure {:yields} {:layer 0} {:refines "atomic_Write"} Write({:linear "tid"} tid: X, i: int, val: bool); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/purity-issta.bpl.expect boogie-2.4.1+dfsg/Test/civl/purity-issta.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/purity-issta.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/purity-issta.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 25 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/refinement.bpl boogie-2.4.1+dfsg/Test/civl/refinement.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/refinement.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/refinement.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,54 @@ +// RUN: %boogie -noinfer "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 0,2} x:int; + +procedure {:yields}{:layer 1}{:refines "INCR"} p () +{ + yield; + call incr(); // Refined action INCR occurred + yield; + call incr(); // State changed again + yield; // Error: Transition invariant in final state violated +} + +procedure {:yields}{:layer 1}{:refines "INCR"} q () +{ + yield; + call decr(); // State changed, but not according to INCR + yield; // Error: Transition invariant in initial state violated + call incr(); + yield; +} + +procedure {:yields}{:layer 1}{:refines "INCR"} r () +{ + yield; + call incr(); + call decr(); // SKIP + yield; + call incr(); // INCR + yield; + call incr(); + call incr(); + call decr(); + call decr(); // SKIP + yield; + +} + +procedure {:both} {:layer 1,2} INCR () +modifies x; +{ + x := x + 1; +} + + +procedure {:both} {:layer 1,2} DECR () +modifies x; +{ + x := x - 1; +} + +procedure {:yields} {:layer 0} {:refines "INCR"} incr (); +procedure {:yields} {:layer 0} {:refines "DECR"} decr (); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/refinement.bpl.expect boogie-2.4.1+dfsg/Test/civl/refinement.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/refinement.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/refinement.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,24 @@ +(0,0): Error: Transition invariant violated in final state +Execution trace: + refinement.bpl(8,3): anon0 + refinement.bpl(8,3): anon0_2 + refinement.bpl(43,5): inline$INCR_1$1$anon0 + refinement.bpl(8,3): anon0_1 + refinement.bpl(43,5): inline$INCR_1$0$anon0 + (0,0): YieldChecker +(0,0): Error: Transition invariant violated in initial state +Execution trace: + refinement.bpl(17,3): anon0 + refinement.bpl(17,3): anon0_2 + refinement.bpl(50,5): inline$DECR_1$0$anon0 + (0,0): YieldChecker +(0,0): Error: Transition invariant violated in final state +Execution trace: + refinement.bpl(17,3): anon0 + refinement.bpl(17,3): anon0_2 + refinement.bpl(50,5): inline$DECR_1$0$anon0 + refinement.bpl(17,3): anon0_1 + refinement.bpl(43,5): inline$INCR_1$0$anon0 + (0,0): YieldChecker + +Boogie program verifier finished with 8 verified, 3 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/reserve.bpl boogie-2.4.1+dfsg/Test/civl/reserve.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/reserve.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/reserve.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,169 @@ +const memLo: int; +const memHi: int; +axiom 0 < memLo && memLo <= memHi; +function memAddr(i:int) returns (bool) { memLo <= i && i < memHi } + +const numMutators: int; +axiom 0 < numMutators; +function {:inline} mutatorAddr(i: int) returns (bool) { 1 <= i && i <= numMutators } + +const GcTid: int; +axiom GcTid > numMutators; + +function {:builtin "MapConst"} MapConstBool(bool): [X]bool; +function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool +{ + MapConstBool(false)[x := true] +} +function {:inline} {:linear "tid"} TidSetCollector(x: [X]bool) : [X]bool +{ + x +} + +function {:builtin "MapOr"} MapOr([X]bool, [X]bool) : [X]bool; +function {:builtin "MapNot"} MapNot(x: [int]bool) : [int]bool; +function {:inline} Subset(X: [int]bool, Y: [int]bool) : (bool) +{ + MapOr(MapNot(X), Y) == MapConstBool(true) +} + +type X = int; + +var {:layer 0,1} Free: [int]bool; +var {:layer 0,1} freeSpace: int; +var {:layer 0,1} AllocatingAtOrAfter: [int][X]bool; +var {:layer 0,1} NumFreeAtOrAfter: [int]int; + +function Size([int]bool) returns (int); +axiom (forall X: [int]bool :: 0 <= Size(X)); +axiom (forall X: [int]bool, x: int :: X[x] ==> 1 <= Size(X)); +axiom (forall X: [int]bool, x: int :: X[x] ==> X[x:=true] == X); +axiom (forall X: [int]bool, x: int :: !X[x] ==> X[x:=false] == X); +axiom (forall X: [int]bool, x: int :: Size(X[x := false]) + 1 == Size(X[x := true])); +axiom (forall X, Y: [int]bool :: Subset(X, Y) ==> Size(X) < Size(Y) || X == Y); + +function {:inline} Invariant(NumFreeAtOrAfter: [int]int, AllocatingAtOrAfter: [int][X]bool, Free: [int]bool, freeSpace: int) : (bool) +{ + Size(AllocatingAtOrAfter[memLo]) + freeSpace == NumFreeAtOrAfter[memLo] && + (forall u: int :: Size(AllocatingAtOrAfter[u]) <= NumFreeAtOrAfter[u]) && + 0 <= freeSpace && + (forall u: int, v: int :: memAddr(u) && memAddr(v) && u <= v ==> Subset(AllocatingAtOrAfter[v], AllocatingAtOrAfter[u])) && + (forall u: int :: memAddr(u) || NumFreeAtOrAfter[u] == 0) && + (forall u: int :: {memAddr(u)} memAddr(u) ==> NumFreeAtOrAfter[u] == (NumFreeAtOrAfter[u+1] + (if Free[u] then 1 else 0))) +} + +procedure {:yields} {:layer 0,1} DecrementFreeSpace({:cnst "tid"} tid: X); +ensures {:atomic} |{ A: assert AllocatingAtOrAfter[memLo] == AllocatingAtOrAfter[memLo][tid := false]; + assume 0 < freeSpace; + freeSpace := freeSpace - 1; + AllocatingAtOrAfter[memLo][tid] := true; + return true; }|; + +procedure {:yields} {:layer 0,1} AllocIfPtrFree({:cnst "tid"} tid:int, ptr:int) returns (spaceFound:bool); +ensures {:atomic} |{ + A: assert memAddr(ptr); + assert Free[ptr] || memAddr(ptr + 1); + assert (forall u: int :: AllocatingAtOrAfter[u][tid] <==> memLo <= u && u <= ptr); + spaceFound := Free[ptr]; + goto B, C; + B: assume (spaceFound); + Free[ptr] := false; + NumFreeAtOrAfter := (lambda u: int :: NumFreeAtOrAfter[u] - (if memLo <= u && u <= ptr then 1 else 0)); + AllocatingAtOrAfter := (lambda u: int :: AllocatingAtOrAfter[u][tid := false]); + return true; + C: assume (!spaceFound); + AllocatingAtOrAfter[ptr+1][tid] := true; + return true; }|; + +procedure {:yields} {:layer 0,1} Reclaim({:cnst "tid"} tid:int); +ensures {:atomic} |{ var ptr: int; + A: assume memAddr(ptr) && !Free[ptr]; + freeSpace := freeSpace + 1; + Free[ptr] := true; + NumFreeAtOrAfter := (lambda u: int :: NumFreeAtOrAfter[u] + (if memLo <= u && u <= ptr then 1 else 0)); + return true; }|; + +procedure {:yields} {:layer 1} YieldAlloc({:cnst "tid"} tid:int, i: int) +requires {:layer 1} mutatorAddr(tid); +requires {:expand} {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); +requires {:layer 1} (forall u: int :: AllocatingAtOrAfter[u][tid] <==> memLo <= u && u <= i); +ensures {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); +ensures {:layer 1} (forall u: int :: AllocatingAtOrAfter[u][tid] <==> memLo <= u && u <= i); +{ + yield; + assert {:layer 1} mutatorAddr(tid); + assert {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); + assert {:layer 1} (forall u: int :: {AllocatingAtOrAfter[u][tid]} AllocatingAtOrAfter[u][tid] <==> memLo <= u && u <= i); +} + +procedure {:yields} {:layer 1} Malloc({:cnst "tid"} tid: X) +requires {:layer 1} mutatorAddr(tid); +requires {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); +requires {:layer 1} (forall u: int :: !AllocatingAtOrAfter[u][tid]); +ensures {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); +ensures {:layer 1} (forall u: int :: !AllocatingAtOrAfter[u][tid]); +{ + var i: int; + var spaceFound: bool; + + call YieldAlloc(tid, 0); + + assert {:layer 1} memAddr(memLo) ==> (forall v: int :: memAddr(v) && memLo <= v ==> Subset(AllocatingAtOrAfter[v], AllocatingAtOrAfter[memLo])); + assert {:layer 1} memAddr(memLo) ==> (forall v: int :: memAddr(v) && v <= memLo ==> Subset(AllocatingAtOrAfter[memLo], AllocatingAtOrAfter[v])); + + call DecrementFreeSpace(tid); + i := memLo; + + call YieldAlloc(tid, i); + + while (i < memHi) + invariant {:layer 1} memAddr(i); + invariant {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); + invariant {:layer 1} (forall u: int :: AllocatingAtOrAfter[u][tid] <==> memLo <= u && u <= i); + { + assert {:layer 1} memAddr(i+1) ==> (forall v: int :: memAddr(v) && i+1 <= v ==> Subset(AllocatingAtOrAfter[v], AllocatingAtOrAfter[i+1])); + assert {:layer 1} memAddr(i+1) ==> (forall v: int :: memAddr(v) && v <= i+1 ==> Subset(AllocatingAtOrAfter[i+1], AllocatingAtOrAfter[v])); + + call spaceFound := AllocIfPtrFree(tid, i); + + if (spaceFound) + { + call YieldAlloc(tid, 0); + return; + } + else + { + i := i + 1; + } + call YieldAlloc(tid, i); + } + + yield; + assert {:layer 1} false; +} + +procedure {:yields} {:layer 1} YieldCollect({:cnst "tid"} tid: X) +requires {:layer 1} tid == GcTid; +requires {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); +ensures {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); +{ + yield; + assert {:layer 1} tid == GcTid; + assert {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); +} + +procedure {:yields} {:layer 1} Collect({:cnst "tid"} tid: X) +requires {:layer 1} tid == GcTid; +requires {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); +ensures {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); +{ + call YieldCollect(tid); + + while (*) + invariant {:layer 1} Invariant(NumFreeAtOrAfter, AllocatingAtOrAfter, Free, freeSpace); + { + call Reclaim(tid); + call YieldCollect(tid); + } + call YieldCollect(tid); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/seqlock.bpl boogie-2.4.1+dfsg/Test/civl/seqlock.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/seqlock.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/seqlock.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,211 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type Tid; +const nil:Tid; + +var {:layer 0,2} lock:Tid; +var {:layer 0,2} seq:int; +var {:layer 0,3} x:int; +var {:layer 0,3} y:int; + +function {:inline 1} isEven (x:int) : bool { x mod 2 == 0 } +function {:inline 1} isOdd (x:int) : bool { x mod 2 != 0 } + +// ============================================================================= +// Implementation of atomic read and write operations (to variables x and y) +// using a seqlock (comprising variables lock and seq). + +procedure {:atomic}{:layer 3} READ () returns (v:int, w:int) +{ + v := x; + w := y; +} + +procedure {:atomic}{:layer 3} WRITE ({:linear "tid"} tid:Tid, v:int, w:int) +modifies x, y; +{ + x := v; + y := w; +} + +procedure {:layer 2}{:yields}{:refines "READ"} read () returns (v:int, w:int) +{ + var seq1:int; + var seq2:int; + yield; + while (true) { + yield; + call seq1 := stale_read_seq(); + if (isEven(seq1)) { + call v := stale_read_x(seq1); + call w := stale_read_y(seq1); + call seq2 := read_seq(); + if (seq1 == seq2) { + yield; + return; + } + } + yield; + } + yield; +} + +procedure {:layer 2}{:yields}{:refines "WRITE"} write ({:linear "tid"} tid:Tid, v:int, w:int) +requires {:layer 2} tid != nil; +requires {:layer 2} lock == nil <==> isEven(seq); +ensures {:layer 2} lock == nil <==> isEven(seq); +{ + yield; + assert {:layer 2} tid != nil; + assert {:layer 2} lock == nil <==> isEven(seq); + call acquire(tid); + call locked_inc_seq(tid); + call locked_write_x(tid, v); + call locked_write_y(tid, w); + yield; + assert {:layer 2} tid != nil && lock == tid; + assert {:layer 2} lock == nil <==> isEven(seq); + call locked_inc_seq(tid); + call release(tid); + yield; + assert {:layer 2} lock == nil <==> isEven(seq); +} + +// ============================================================================= +// Abstractions of atomic actions with stronger mover types +// Key insights: +// * First read of seq can be stale (i.e., less than the actual value), and +// reads of x and y only return the correct value in case the previously read +// value of seq was even and is still the actual value (otherwise any value +// can be returned). +// * Increments of seq and writes of x and y are lock-protected, and writes only +// happen when seq is odd. + +procedure {:right}{:layer 2} STALE_READ_SEQ () returns (r:int) +{ + assume r <= seq; +} + +procedure {:right}{:layer 2} STALE_READ_X (seq1:int) returns (r:int) +{ + assert seq >= seq1; + if (isEven(seq) && seq == seq1) { + r := x; + } +} + +procedure {:right}{:layer 2} STALE_READ_Y (seq1:int) returns (r:int) +{ + assert seq >= seq1; + if (isEven(seq) && seq == seq1) { + r := y; + } +} + +procedure {:atomic}{:layer 2} LOCKED_INC_SEQ ({:linear "tid"} tid:Tid) +modifies seq; +{ + assert tid != nil && lock == tid; + seq := seq + 1; +} + +procedure {:both}{:layer 2} LOCKED_WRITE_X ({:linear "tid"} tid:Tid, v:int) +modifies x; +{ + assert isOdd(seq); + assert tid != nil && lock == tid; + x := v; +} + +procedure {:both}{:layer 2} LOCKED_WRITE_Y ({:linear "tid"} tid:Tid, v:int) +modifies y; +{ + assert isOdd(seq); + assert tid != nil && lock == tid; + y := v; +} + +procedure {:yields}{:layer 1}{:refines "STALE_READ_SEQ"} stale_read_seq () returns (r:int) +{ yield; call r := read_seq(); yield; } +procedure {:yields}{:layer 1}{:refines "STALE_READ_X"} stale_read_x ({:layer 1} seq1:int) returns (r:int) +{ yield; call r := read_x(); yield; } +procedure {:yields}{:layer 1}{:refines "STALE_READ_Y"} stale_read_y ({:layer 1} seq1:int) returns (r:int) +{ yield; call r := read_y(); yield; } +procedure {:yields}{:layer 1}{:refines "LOCKED_INC_SEQ"} locked_inc_seq ({:layer 1}{:linear "tid"} tid:Tid) +{ yield; call inc_seq(); yield; } +procedure {:yields}{:layer 1}{:refines "LOCKED_WRITE_X"} locked_write_x ({:layer 1}{:linear "tid"} tid:Tid, v:int) +{ yield; call write_x(v); yield; } +procedure {:yields}{:layer 1}{:refines "LOCKED_WRITE_Y"} locked_write_y ({:layer 1}{:linear "tid"} tid:Tid, v:int) +{ yield; call write_y(v); yield; } + +// ============================================================================= +// Primitie atomic actions +// * read and write of x and y +// * read and increment of seq +// * acquire and release of lock + +procedure {:atomic}{:layer 1} READ_X () returns (r:int) +{ + r := x; +} + +procedure {:atomic}{:layer 1} READ_Y () returns (r:int) +{ + r := y; +} + +procedure {:atomic}{:layer 1} WRITE_X (v:int) +modifies x; +{ + x := v; +} + +procedure {:atomic}{:layer 1} WRITE_Y (v:int) +modifies y; +{ + y := v; +} + +procedure {:atomic}{:layer 1,2} READ_SEQ () returns (r:int) +{ + r := seq; +} + +procedure {:atomic}{:layer 1} INC_SEQ () +modifies seq; +{ + seq := seq + 1; +} + +procedure {:right}{:layer 1,2} ACQUIRE ({:linear "tid"} tid:Tid) +modifies lock; +{ + assert tid != nil; + assume lock == nil; + lock := tid; +} + +procedure {:left}{:layer 1,2} RELEASE ({:linear "tid"} tid:Tid) +modifies lock; +{ + assert tid != nil && lock == tid; + lock := nil; +} + +procedure {:yields}{:layer 0}{:refines "READ_X"} read_x () returns (r:int); +procedure {:yields}{:layer 0}{:refines "READ_Y"} read_y () returns (r:int); +procedure {:yields}{:layer 0}{:refines "WRITE_X"} write_x (v:int); +procedure {:yields}{:layer 0}{:refines "WRITE_Y"} write_y (v:int); +procedure {:yields}{:layer 0}{:refines "READ_SEQ"} read_seq () returns (r:int); +procedure {:yields}{:layer 0}{:refines "INC_SEQ"} inc_seq (); +procedure {:yields}{:layer 0}{:refines "ACQUIRE"} acquire ({:linear "tid"} tid:Tid); +procedure {:yields}{:layer 0}{:refines "RELEASE"} release ({:linear "tid"} tid:Tid); + +// ============================================================================= + +function {:builtin "MapConst"} MapConstBool(bool) : [Tid]bool; +function {:inline}{:linear "tid"} TidCollector(tid:Tid) : [Tid]bool +{ + MapConstBool(false)[tid := true] +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/seqlock.bpl.expect boogie-2.4.1+dfsg/Test/civl/seqlock.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/seqlock.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/seqlock.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 57 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Siddharth-queue.bpl boogie-2.4.1+dfsg/Test/civl/Siddharth-queue.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Siddharth-queue.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/Siddharth-queue.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,72 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +type Method; + +type Invoc; + +// Sequences of invocations +type SeqInvoc; +function Seq_append(s: SeqInvoc, o: Invoc) returns (t: SeqInvoc); + +// Sets of invocations +type SetInvoc; +function Set_ofSeq(q: SeqInvoc) returns (s: SetInvoc); + + +var {:layer 1,2} lin: SeqInvoc; +var {:layer 1,2} vis: [Invoc]SetInvoc; + +type Ref; +type Key; + +// ---------- Primitives for manipulating logical/abstract state + +procedure {:layer 1} intro_readLin() returns (s: SetInvoc) + ensures {:layer 1} s == Set_ofSeq(lin); +{ + s := Set_ofSeq(lin); +} + +procedure {:layer 1} intro_write_vis(n: Invoc, s: SetInvoc) + modifies vis; + ensures {:layer 1} vis == old(vis)[n := s]; +{ + vis[n] := s; +} + +procedure {:layer 1} intro_writeLin(n: Invoc) + ensures {:layer 1} lin == Seq_append(old(lin), n); + modifies lin; +{ + lin := Seq_append(lin, n); +} + +// ---------- Specification program: + +procedure {:atomic} {:layer 2} pop_atomic(this: Invoc) returns (k: Key) + modifies lin, vis; +{ + var my_vis: SetInvoc; + lin := Seq_append(lin, this); + assume my_vis == Set_ofSeq(lin); + // buggy transition relation computation due to assume after assignment to lin which + // creates difference between lin and old(lin) + vis[this] := my_vis; +} + +// ---------- Implementation: + +procedure {:yields} {:layer 1} {:refines "pop_atomic"} pop(this: Invoc) + returns (k: Key) +{ + var {:layer 1} my_vis: SetInvoc; + + yield; + + call intro_writeLin(this); + call my_vis := intro_readLin(); + call intro_write_vis(this, my_vis); + assert {:layer 1} my_vis == Set_ofSeq(lin); // Despite this assertion passing + + yield; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Siddharth-queue.bpl.expect boogie-2.4.1+dfsg/Test/civl/Siddharth-queue.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/Siddharth-queue.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/Siddharth-queue.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 4 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/signature-mismatch.bpl boogie-2.4.1+dfsg/Test/civl/signature-mismatch.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/signature-mismatch.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/signature-mismatch.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,36 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +var {:layer 0,1} x:int; + +//////////////////////////////////////////////////////////////////////////////// + +procedure {:yields} {:layer 1} main () +{ + yield; + call write_x_1(true); + yield; +} + +procedure {:atomic} {:layer 1,1} atomic_write_x_1 (x':int) +modifies x; +{ x := x'; } + +procedure {:yields} {:layer 0} {:refines "atomic_write_x_1"} write_x_1 (y':bool); + +//////////////////////////////////////////////////////////////////////////////// + +procedure {:atomic} {:layer 1,1} atomic_write_x_2 (x':int, foo:int) +modifies x; +{ x := x'; } + +procedure {:yields} {:layer 0} {:refines "atomic_write_x_2"} write_x_2 (y':bool); + + +//////////////////////////////////////////////////////////////////////////////// + +procedure {:atomic} {:layer 1,1} atomic_write_x_3 ({:linear "lin"} x':int) +modifies x; +{ x := x'; } + +procedure {:yields} {:layer 0} {:refines "atomic_write_x_3"} write_x_3 ({:linear_in "lin"} x':int); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/signature-mismatch.bpl.expect boogie-2.4.1+dfsg/Test/civl/signature-mismatch.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/signature-mismatch.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/signature-mismatch.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,4 @@ +signature-mismatch.bpl(19,61): Error: mismatched type of in-parameter in refinement procedure write_x_1: y' (named x' in atomic action) +signature-mismatch.bpl(27,61): Error: mismatched number of in-parameters in refinement procedure: write_x_2 +signature-mismatch.bpl(36,61): Error: mismatched linearity type of in-parameter in refinement procedure write_x_3: x' +3 type checking errors detected in signature-mismatch.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/StoreBuffer.bpl boogie-2.4.1+dfsg/Test/civl/StoreBuffer.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/StoreBuffer.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/StoreBuffer.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -29,9 +29,9 @@ var {:layer 0,1} StoreBufferVal: [int][int]int; var {:layer 0,1} StoreBufferPresent: [int][int]bool; -var {:layer 0} lock: int; -var {:layer 0} collectorPhase: int; -var {:layer 0} collectorPhaseDelayed: int; +var {:layer 0,2} lock: int; +var {:layer 0,2} collectorPhase: int; +var {:layer 0,2} collectorPhaseDelayed: int; function {:inline} LockInv(StoreBufferPresent:[int][int]bool, StoreBufferVal:[int][int]int, Mem:[int]int, lock:int, collectorPhase:int, collectorPhaseDelayed:int): bool { @@ -68,11 +68,14 @@ assert {:layer 1} !StoreBufferPresent[tid][lockAddr]; } -procedure {:yields} {:layer 1} LockAcquire({:linear "tid"} tid: int) +procedure {:right} {:layer 2} AtomicLockAcquire({:linear "tid"} tid: int) +modifies lock; +{ assert mutatorOrGcTid(tid); assume lock == 0; lock := tid; } + +procedure {:yields} {:layer 1} {:refines "AtomicLockAcquire"} LockAcquire({:linear "tid"} tid: int) requires {:layer 1} mutatorOrGcTid(tid); requires {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); ensures {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); -ensures {:right} |{ A: assert mutatorOrGcTid(tid); assume lock == 0; lock := tid; return true; }|; { var status:bool; call YieldLock(); @@ -82,21 +85,24 @@ call status := LockCAS(tid); if (status) { - call YieldLock(); + call YieldLock(); return; } - call YieldLock(); + call YieldLock(); } call YieldLock(); } -procedure {:yields} {:layer 1} LockRelease({:linear "tid"} tid:int) +procedure {:atomic} {:layer 2} AtomicLockRelease({:linear "tid"} tid:int) +modifies lock; +{ assert mutatorOrGcTid(tid); assert lock == tid; lock := 0; } + +procedure {:yields} {:layer 1} {:refines "AtomicLockRelease"} LockRelease({:linear "tid"} tid:int) requires {:layer 1} mutatorOrGcTid(tid); requires {:layer 1} !StoreBufferPresent[tid][lockAddr]; requires {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); ensures {:layer 1} !StoreBufferPresent[tid][lockAddr]; ensures {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); -ensures {:atomic} |{ A: assert mutatorOrGcTid(tid); assert lock == tid; lock := 0; return true; }|; { par YieldLock() | YieldStoreBufferLockAddrAbsent(tid); call LockZero(tid); @@ -105,54 +111,66 @@ par YieldLock() | YieldStoreBufferLockAddrAbsent(tid); } -procedure {:yields} {:layer 1} ReadCollectorPhaseLocked({:linear "tid"} tid: int) returns (phase: int) +procedure {:atomic} {:layer 2} AtomicReadCollectorPhaseLocked({:linear "tid"} tid: int) returns (phase: int) +{ assert mutatorOrGcTid(tid); assert lock == tid; phase := collectorPhase; } + +procedure {:yields} {:layer 1} {:refines "AtomicReadCollectorPhaseLocked"} ReadCollectorPhaseLocked({:linear "tid"} tid: int) returns (phase: int) requires {:layer 1} mutatorOrGcTid(tid); requires {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); ensures {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); -ensures {:atomic} |{ A: assert mutatorOrGcTid(tid); assert lock == tid; phase := collectorPhase; return true; }|; { call YieldLock(); call phase := PrimitiveRead(tid, collectorPhaseAddr); - call YieldLock(); + call YieldLock(); } -procedure {:yields} {:layer 1} ReadCollectorPhaseUnlocked({:linear "tid"} tid: int) returns (phase: int) +procedure {:atomic} {:layer 2} AtomicReadCollectorPhaseUnlocked({:linear "tid"} tid: int) returns (phase: int) +{ assert mutatorOrGcTid(tid); assert lock != tid; phase := collectorPhaseDelayed; } + +procedure {:yields} {:layer 1} {:refines "AtomicReadCollectorPhaseUnlocked"} ReadCollectorPhaseUnlocked({:linear "tid"} tid: int) returns (phase: int) requires {:layer 1} mutatorOrGcTid(tid); requires {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); ensures {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); -ensures {:atomic} |{ A: assert mutatorOrGcTid(tid); assert lock != tid; phase := collectorPhaseDelayed; return true; }|; { call YieldLock(); call phase := PrimitiveRead(tid, collectorPhaseAddr); - call YieldLock(); + call YieldLock(); } -procedure {:yields} {:layer 1} SetCollectorPhase({:linear "tid"} tid: int, phase: int) +procedure {:atomic} {:layer 2} AtomicSetCollectorPhase({:linear "tid"} tid: int, phase: int) +modifies collectorPhase; +{ assert mutatorOrGcTid(tid); assert lock == tid; assert collectorPhase == collectorPhaseDelayed; collectorPhase := phase; } + +procedure {:yields} {:layer 1} {:refines "AtomicSetCollectorPhase"} SetCollectorPhase({:linear "tid"} tid: int, phase: int) requires {:layer 1} mutatorOrGcTid(tid); requires {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); ensures {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); -ensures {:atomic} |{ A: assert mutatorOrGcTid(tid); assert lock == tid; assert collectorPhase == collectorPhaseDelayed; collectorPhase := phase; return true; }|; { call YieldLock(); call PrimitiveSetCollectorPhase(tid, phase); call YieldLock(); } -procedure {:yields} {:layer 1} SyncCollectorPhase({:linear "tid"} tid: int) +procedure {:atomic} {:layer 2} AtomicSyncCollectorPhase({:linear "tid"} tid: int) +modifies collectorPhaseDelayed; +{ collectorPhaseDelayed := collectorPhase; } + +procedure {:yields} {:layer 1} {:refines "AtomicSyncCollectorPhase"} SyncCollectorPhase({:linear "tid"} tid: int) requires {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); ensures {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); -ensures {:atomic} |{ A: collectorPhaseDelayed := collectorPhase; return true; }|; { call YieldLock(); call FlushStoreBufferEntryForCollectorPhase(); call YieldLock(); } -procedure {:yields} {:layer 1} Barrier({:linear "tid"} tid: int) +procedure {:atomic} {:layer 2} AtomicBarrier({:linear "tid"} tid: int) +{ assert mutatorOrGcTid(tid); assert lock == tid; assume collectorPhase == collectorPhaseDelayed; } + +procedure {:yields} {:layer 1} {:refines "AtomicBarrier"} Barrier({:linear "tid"} tid: int) requires {:layer 1} mutatorOrGcTid(tid); requires {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); ensures {:layer 1} LockInv(StoreBufferPresent, StoreBufferVal, Mem, lock, collectorPhase, collectorPhaseDelayed); -ensures {:atomic} |{ A: assert mutatorOrGcTid(tid); assert lock == tid; assume collectorPhase == collectorPhaseDelayed; return true; }|; { call YieldLock(); call WaitForFlush(tid); @@ -160,28 +178,69 @@ } // Layer 0 -procedure {:yields} {:layer 0,1} LockCAS(tid: int) returns (status: bool); -ensures {:atomic} |{ A: goto B, C; - B: assume Mem[lockAddr] == 0; Mem[lockAddr] := 1; lock := tid; status := true; return true; - C: status := false; return true; - }|; - -procedure {:yields} {:layer 0,1} LockZero(tid: int); -ensures {:atomic} |{ A: assert !StoreBufferPresent[tid][lockAddr]; StoreBufferPresent[tid][lockAddr] := true; StoreBufferVal[tid][lockAddr] := 0; return true; }|; - -procedure {:yields} {:layer 0,1} FlushStoreBufferEntryForLock(tid: int); -ensures {:atomic} |{ A: assert StoreBufferPresent[tid][lockAddr]; assume StoreBufferPresent[tid] == MapConstBool(false)[lockAddr := true]; Mem[lockAddr] := StoreBufferVal[tid][lockAddr]; StoreBufferPresent[tid][lockAddr] := false; lock := 0; return true; }|; - -procedure {:yields} {:layer 0,1} PrimitiveRead(tid: int, addr: int) returns (val: int); -ensures {:atomic} |{ A: goto B, C; - B: assume StoreBufferPresent[tid][addr]; val := StoreBufferVal[tid][addr]; return true; - C: assume !StoreBufferPresent[tid][addr]; val := Mem[addr]; return true; }|; +procedure {:atomic} {:layer 1} AtomicLockCAS(tid: int) returns (status: bool) +modifies Mem, lock; +{ + if (*) { + assume Mem[lockAddr] == 0; + Mem[lockAddr] := 1; + lock := tid; + status := true; + } else { + status := false; + } +} + +procedure {:yields} {:layer 0} {:refines "AtomicLockCAS"} LockCAS(tid: int) returns (status: bool); + +procedure {:atomic} {:layer 1} AtomicLockZero(tid: int) +modifies StoreBufferPresent, StoreBufferVal; +{ assert !StoreBufferPresent[tid][lockAddr]; StoreBufferPresent[tid][lockAddr] := true; StoreBufferVal[tid][lockAddr] := 0; } + +procedure {:yields} {:layer 0} {:refines "AtomicLockZero"} LockZero(tid: int); + +procedure {:atomic} {:layer 1} AtomicFlushStoreBufferEntryForLock(tid: int) +modifies Mem, StoreBufferPresent, lock; +{ + assert StoreBufferPresent[tid][lockAddr]; + assume StoreBufferPresent[tid] == MapConstBool(false)[lockAddr := true]; + Mem[lockAddr] := StoreBufferVal[tid][lockAddr]; + StoreBufferPresent[tid][lockAddr] := false; + lock := 0; +} + +procedure {:yields} {:layer 0} {:refines "AtomicFlushStoreBufferEntryForLock"} FlushStoreBufferEntryForLock(tid: int); + +procedure {:atomic} {:layer 1} AtomicPrimitiveRead(tid: int, addr: int) returns (val: int) +{ + if (StoreBufferPresent[tid][addr]) { + val := StoreBufferVal[tid][addr]; + } else { + val := Mem[addr]; + } +} + +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveRead"} PrimitiveRead(tid: int, addr: int) returns (val: int); + +procedure {:atomic} {:layer 1} AtomicPrimitiveSetCollectorPhase(tid: int, phase:int) +modifies StoreBufferPresent, StoreBufferVal, collectorPhase; +{ StoreBufferPresent[tid][collectorPhaseAddr] := true; StoreBufferVal[tid][collectorPhaseAddr] := phase; collectorPhase := phase; } + +procedure {:yields} {:layer 0} {:refines "AtomicPrimitiveSetCollectorPhase"} PrimitiveSetCollectorPhase(tid: int, phase:int); + +procedure {:atomic} {:layer 1} AtomicFlushStoreBufferEntryForCollectorPhase() +modifies Mem, StoreBufferPresent, collectorPhaseDelayed; +{ + var tid:int; + assume mutatorOrGcTid(tid) && StoreBufferPresent[tid][collectorPhaseAddr]; + Mem[collectorPhaseAddr] := StoreBufferVal[tid][collectorPhaseAddr]; + StoreBufferPresent[tid][collectorPhaseAddr] := false; + collectorPhaseDelayed := Mem[collectorPhaseAddr]; +} -procedure {:yields} {:layer 0,1} PrimitiveSetCollectorPhase(tid: int, phase:int); -ensures {:atomic} |{ A: StoreBufferPresent[tid][collectorPhaseAddr] := true; StoreBufferVal[tid][collectorPhaseAddr] := phase; collectorPhase := phase; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicFlushStoreBufferEntryForCollectorPhase"} FlushStoreBufferEntryForCollectorPhase(); -procedure {:yields} {:layer 0,1} FlushStoreBufferEntryForCollectorPhase(); -ensures {:atomic} |{ var tid:int; A: assume mutatorOrGcTid(tid) && StoreBufferPresent[tid][collectorPhaseAddr]; Mem[collectorPhaseAddr] := StoreBufferVal[tid][collectorPhaseAddr]; StoreBufferPresent[tid][collectorPhaseAddr] := false; collectorPhaseDelayed := Mem[collectorPhaseAddr]; return true; }|; +procedure {:atomic} {:layer 1} AtomicWaitForFlush(tid: int) +{ assume StoreBufferPresent[tid] == MapConstBool(false); } -procedure {:yields} {:layer 0,1} WaitForFlush(tid: int); -ensures {:atomic} |{ A: assume StoreBufferPresent[tid] == MapConstBool(false); return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicWaitForFlush"} WaitForFlush(tid: int); \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/StoreBuffer.bpl.expect boogie-2.4.1+dfsg/Test/civl/StoreBuffer.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/StoreBuffer.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/StoreBuffer.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 27 verified, 0 errors +Boogie program verifier finished with 34 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/t1.bpl boogie-2.4.1+dfsg/Test/civl/t1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/t1.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/t1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -7,6 +7,10 @@ { MapConstBool(false)[x := true] } +function {:inline} {:linear "tid"} TidSetCollector(x: [int]bool) : [int]bool +{ + x +} function {:inline} {:linear "1"} SetCollector1(x: [int]bool) : [int]bool { @@ -21,18 +25,26 @@ var {:layer 0,1} g: int; var {:layer 0,1} h: int; -procedure {:yields} {:layer 0,1} SetG(val:int); -ensures {:atomic} |{A: g := val; return true; }|; +var {:layer 0,1}{:linear "tid"} unallocated:[int]bool; + +procedure {:atomic} {:layer 1} AtomicSetG(val:int) +modifies g; +{ g := val; } -procedure {:yields} {:layer 0,1} SetH(val:int); -ensures {:atomic} |{A: h := val; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicSetG"} SetG(val:int); + +procedure {:atomic} {:layer 1} AtomicSetH(val:int) +modifies h; +{ h := val; } + +procedure {:yields} {:layer 0} {:refines "AtomicSetH"} SetH(val:int); procedure {:yields} {:layer 1} Yield({:linear "1"} x: [int]bool) -requires {:layer 1} x == mapconstbool(true) && g == 0; -ensures {:layer 1} x == mapconstbool(true) && g == 0; +requires {:layer 1} x == mapconstbool(true) && g == 0; +ensures {:layer 1} x == mapconstbool(true) && g == 0; { yield; - assert {:layer 1} x == mapconstbool(true) && g == 0; + assert {:layer 1} x == mapconstbool(true) && g == 0; } procedure {:yields} {:layer 1} Allocate() returns ({:linear "tid"} xl: int) @@ -43,10 +55,13 @@ yield; } -procedure {:yields} {:layer 0,1} AllocateLow() returns ({:linear "tid"} xls: int); -ensures {:atomic} |{ A: assume xls != 0; return true; }|; +procedure {:atomic} {:layer 1} AtomicAllocateLow() returns ({:linear "tid"} xls: int) +modifies unallocated; +{ assume xls != 0 && unallocated[xls]; unallocated[xls] := false; } -procedure {:yields} {:layer 1} A({:linear_in "tid"} tid_in: int, {:linear_in "1"} x: [int]bool, {:linear_in "2"} y: [int]bool) returns ({:linear "tid"} tid_out: int) +procedure {:yields} {:layer 0} {:refines "AtomicAllocateLow"} AllocateLow() returns ({:linear "tid"} xls: int); + +procedure {:yields} {:layer 1} A({:linear_in "tid"} tid_in: int, {:linear_in "1"} x: [int]bool, {:linear_in "2"} y: [int]bool) returns ({:linear "tid"} tid_out: int) requires {:layer 1} x == mapconstbool(true); requires {:layer 1} y == mapconstbool(true); { @@ -55,19 +70,19 @@ yield; call SetG(0); - + par tid_child := Allocate() | Yield(x); async call B(tid_child, x); yield; - assert {:layer 1} x == mapconstbool(true); + assert {:layer 1} x == mapconstbool(true); assert {:layer 1} g == 0; call SetH(0); yield; - assert {:layer 1} h == 0 && y == mapconstbool(true); + assert {:layer 1} h == 0 && y == mapconstbool(true); yield; call tid_child := Allocate(); @@ -76,26 +91,26 @@ yield; } -procedure {:yields} {:layer 1} B({:linear_in "tid"} tid_in: int, {:linear_in "1"} x_in: [int]bool) +procedure {:yields} {:layer 1} B({:linear_in "tid"} tid_in: int, {:linear_in "1"} x_in: [int]bool) requires {:layer 1} x_in != mapconstbool(false); { var {:linear "tid"} tid_out: int; var {:linear "1"} x: [int]bool; tid_out := tid_in; - x := x_in; + x := x_in; yield; call SetG(1); yield; } -procedure {:yields} {:layer 1} C({:linear_in "tid"} tid_in: int, {:linear_in "2"} y_in: [int]bool) +procedure {:yields} {:layer 1} C({:linear_in "tid"} tid_in: int, {:linear_in "2"} y_in: [int]bool) requires {:layer 1} y_in != mapconstbool(false); { var {:linear "tid"} tid_out: int; var {:linear "2"} y: [int]bool; tid_out := tid_in; - y := y_in; + y := y_in; yield; call SetH(1); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/t1.bpl.expect boogie-2.4.1+dfsg/Test/civl/t1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/t1.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/t1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,9 +1,9 @@ -t1.bpl(65,5): Error: Non-interference check failed +t1.bpl(80,5): Error: Non-interference check failed Execution trace: - t1.bpl(84,13): anon0 - (0,0): anon05 - (0,0): inline$SetG_1$0$Entry - t1.bpl(25,21): inline$SetG_1$0$A + t1.bpl(99,13): anon0 + t1.bpl(99,13): anon0_1 + t1.bpl(99,13): anon0_1$1 + (0,0): YieldChecker (0,0): inline$Impl_YieldChecker_A_1$0$L1 -Boogie program verifier finished with 9 verified, 1 error +Boogie program verifier finished with 10 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/termination2.bpl boogie-2.4.1+dfsg/Test/civl/termination2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/termination2.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/termination2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -1,10 +1,10 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -procedure {:yields} {:layer 0} X(); -ensures {:atomic} |{ A: return true; }|; +procedure {:atomic} {:layer 1} AtomicX() { } + +procedure {:yields} {:layer 0} {:refines "AtomicX"} X(); procedure {:yields} {:layer 0} Y(); -ensures {:left} |{ A: return true; }|; procedure {:yields} {:layer 1} main() { yield; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/termination.bpl boogie-2.4.1+dfsg/Test/civl/termination.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/termination.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/termination.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -1,10 +1,10 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -procedure {:yields} {:layer 0} X(); -ensures {:atomic} |{ A: return true; }|; +procedure {:atomic} {:layer 1} AtomicX() { } + +procedure {:yields} {:layer 0} {:refines "AtomicX"} X(); procedure {:yields} {:layer 0} Y(); -ensures {:left} |{ A: return true; }|; procedure {:yields} {:layer 1} main() { yield; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/termination.bpl.expect boogie-2.4.1+dfsg/Test/civl/termination.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/termination.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/termination.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,3 +1,2 @@ -termination.bpl(9,31): Error: Implementation main fails simulation check C at layer 1. Transactions must be separated by a yield. - +termination.bpl(9,31): Error: Implementation main fails atomicity check at layer 1. Transactions must be separated by yields. 1 type checking errors detected in termination.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ticket.bpl boogie-2.4.1+dfsg/Test/civl/ticket.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ticket.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/ticket.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -1,157 +1,196 @@ // RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" // RUN: %diff "%s.expect" "%t" -function RightOpen(n: int) : [int]bool; -function RightClosed(n: int) : [int]bool; + +function RightOpen (n: int) : [int]bool; +function RightClosed (n: int) : [int]bool; axiom (forall x: int, y: int :: RightOpen(x)[y] <==> y < x); axiom (forall x: int, y: int :: RightClosed(x)[y] <==> y <= x); type X; const nil: X; -var {:layer 0,1} t: int; -var {:layer 0,2} s: int; -var {:layer 0,2} cs: X; -var {:layer 0,2} T: [int]bool; +var {:layer 0,1} t: int; // next ticket to issue +var {:layer 0,2} s: int; // current ticket permitted to critical section +var {:layer 0,2} cs: X; // current thread in critical section +var {:layer 0,2} T: [int]bool; // set of issued tickets + +// ########################################################################### +// Invariants -function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; -function {:inline} {:linear "tid"} TidCollector(x: X) : [X]bool +function {:inline} Inv1 (tickets: [int]bool, ticket: int): (bool) { - MapConstBool(false)[x := true] + tickets == RightOpen(ticket) } -function {:inline} {:linear "tid"} TidSetCollector(x: [X]bool) : [X]bool + +function {:inline} Inv2 (tickets: [int]bool, ticket: int, lock: X): (bool) { - x + if (lock == nil) then tickets == RightOpen(ticket) + else tickets == RightClosed(ticket) } -function {:inline} Inv1(tickets: [int]bool, ticket: int): (bool) +// ########################################################################### +// Yield assertions + +procedure {:yields} {:layer 2} YieldSpec ({:linear "tid"} tid: X) +requires {:layer 2} tid != nil && cs == tid; +ensures {:layer 2} tid != nil && cs == tid; { - tickets == RightOpen(ticket) + yield; + assert {:layer 2} tid != nil && cs == tid; } -function {:inline} Inv2(tickets: [int]bool, ticket: int, lock: X): (bool) +procedure {:yields} {:layer 1} Yield1 () +requires {:layer 1} Inv1(T, t); +ensures {:layer 1} Inv1(T, t); { - if (lock == nil) then tickets == RightOpen(ticket) else tickets == RightClosed(ticket) + yield; + assert {:layer 1} Inv1(T, t); } -procedure {:yields} {:layer 2} Allocate({:linear_in "tid"} xls':[X]bool) returns ({:linear "tid"} xls: [X]bool, {:linear "tid"} xl: X) -ensures {:layer 1} {:layer 2} xl != nil; +procedure {:yields} {:layer 2} Yield2 () +requires {:layer 2} Inv2(T, s, cs); +ensures {:layer 2} Inv2(T, s, cs); { - yield; - call xls, xl := AllocateLow(xls'); - yield; + yield; + assert {:layer 2} Inv2(T, s, cs); } -procedure {:yields} {:layer 2} main({:linear_in "tid"} xls':[X]bool) -requires {:layer 1} Inv1(T, t); -requires {:layer 2} xls' == MapConstBool(true) && Inv2(T, s, cs); +// ########################################################################### +// Main program + +procedure {:yields} {:layer 2} main ({:linear_in "tid"} xls':[X]bool) +requires {:layer 2} xls' == MapConstBool(true); { - var {:linear "tid"} tid: X; - var {:linear "tid"} xls: [X]bool; + var {:linear "tid"} tid: X; + var {:linear "tid"} xls: [X]bool; - par Yield1() | Yield2(); + yield; - call InitAbstract(xls'); - xls := xls'; + call InitAbstract(xls'); + xls := xls'; - par Yield1() | Yield2(); + par Yield1() | Yield2(); - while (*) - invariant {:layer 1} Inv1(T, t); - invariant {:layer 2} Inv2(T, s, cs); - { - par xls, tid := Allocate(xls) | Yield1() | Yield2(); - async call Customer(tid); - par Yield1() | Yield2(); - } + while (*) + invariant {:layer 1} Inv1(T, t); + invariant {:layer 2} Inv2(T, s, cs); + { + par xls, tid := Allocate(xls) | Yield1() | Yield2(); + async call Customer(tid); par Yield1() | Yield2(); + } + par Yield1() | Yield2(); } -procedure {:yields} {:layer 2} Customer({:linear_in "tid"} tid: X) -requires {:layer 1} Inv1(T, t); -requires {:layer 2} tid != nil && Inv2(T, s, cs); +procedure {:yields} {:layer 2} Allocate ({:linear_in "tid"} xls':[X]bool) returns ({:linear "tid"} xls: [X]bool, {:linear "tid"} xl: X) +ensures {:layer 1,2} xl != nil; { - par Yield1() | Yield2(); - while (*) - invariant {:layer 1} Inv1(T, t); - invariant {:layer 2} Inv2(T, s, cs); - { - call Enter(tid); - par Yield1() | Yield2() | YieldSpec(tid); - call Leave(tid); - par Yield1() | Yield2(); - } - par Yield1() | Yield2(); + yield; + call xls, xl := AllocateLow(xls'); + yield; } -procedure {:yields} {:layer 2} Enter({:linear "tid"} tid: X) +procedure {:yields} {:layer 2} Customer ({:linear_in "tid"} tid: X) requires {:layer 1} Inv1(T, t); -ensures {:layer 1} Inv1(T,t); -requires {:layer 2} tid != nil && Inv2(T, s, cs); -ensures {:layer 2} Inv2(T, s, cs) && cs == tid; +requires {:layer 2} Inv2(T, s, cs) && tid != nil; { - var m: int; - - par Yield1() | Yield2(); - call m := GetTicketAbstract(tid); - par Yield1(); - call WaitAndEnter(tid, m); + par Yield1() | Yield2(); + while (*) + invariant {:layer 1} Inv1(T, t); + invariant {:layer 2} Inv2(T, s, cs); + { + call Enter(tid); par Yield1() | Yield2() | YieldSpec(tid); + call Leave(tid); + par Yield1() | Yield2(); + } + par Yield1() | Yield2(); } -procedure {:yields} {:layer 1,2} InitAbstract({:linear "tid"} xls:[X]bool) +procedure {:yields} {:layer 2} Enter ({:linear "tid"} tid: X) requires {:layer 1} Inv1(T, t); -ensures {:layer 1} Inv1(T, t); -ensures {:atomic} |{ A: assert xls == MapConstBool(true); cs := nil; s := 0; T := RightOpen(0); return true; }|; +ensures {:layer 1} Inv1(T, t); +requires {:layer 2} Inv2(T, s, cs) && tid != nil; +ensures {:layer 2} Inv2(T, s, cs) && cs == tid; { - par Yield1(); - call Init(xls); - par Yield1(); -} + var m: int; -procedure {:yields} {:layer 1,2} GetTicketAbstract({:linear "tid"} tid: X) returns (m: int) -requires {:layer 1} Inv1(T, t); -ensures {:layer 1} Inv1(T, t); -ensures {:right} |{ A: havoc m; assume !T[m]; T[m] := true; return true; }|; -{ - par Yield1(); - call m := GetTicket(tid); - par Yield1(); + par Yield1() | Yield2(); + call m := GetTicketAbstract(tid); + call WaitAndEnter(tid, m); + par Yield1() | Yield2() | YieldSpec(tid); } -procedure {:yields} {:layer 2} YieldSpec({:linear "tid"} tid: X) -requires {:layer 2} tid != nil && cs == tid; -ensures {:layer 2} cs == tid; -{ - yield; - assert {:layer 2} tid != nil && cs == tid; -} +// ########################################################################### +// Abstractions of primitive atomic actions -procedure {:yields} {:layer 2} Yield2() -requires {:layer 2} Inv2(T, s, cs); -ensures {:layer 2} Inv2(T, s, cs); +// Note how GetTicketAbstract becomes a right mover + +procedure {:atomic} {:layer 2} AtomicInitAbstract ({:linear "tid"} xls:[X]bool) +modifies cs, s, T; +{ assert xls == MapConstBool(true); cs := nil; s := 0; T := RightOpen(0); } + +procedure {:yields} {:layer 1} {:refines "AtomicInitAbstract"} InitAbstract ({:linear "tid"} xls:[X]bool) +ensures {:layer 1} Inv1(T, t); { - yield; - assert {:layer 2} Inv2(T, s, cs); + yield; + call Init(xls); + par Yield1(); } -procedure {:yields} {:layer 1} Yield1() +procedure {:right} {:layer 2} AtomicGetTicketAbstract ({:linear "tid"} tid: X) returns (m: int) +modifies T; +{ assume !T[m]; T[m] := true; } + +procedure {:yields} {:layer 1} {:refines "AtomicGetTicketAbstract"} GetTicketAbstract ({:linear "tid"} tid: X) returns (m: int) requires {:layer 1} Inv1(T, t); -ensures {:layer 1} Inv1(T,t); +ensures {:layer 1} Inv1(T, t); { - yield; - assert {:layer 1} Inv1(T,t); + par Yield1(); + call m := GetTicket(tid); + par Yield1(); } -procedure {:yields} {:layer 0,1} Init({:linear "tid"} xls:[X]bool); -ensures {:atomic} |{ A: assert xls == MapConstBool(true); cs := nil; t := 0; s := 0; T := RightOpen(0); return true; }|; +// ########################################################################### +// Primitive atomic actions + +procedure {:atomic} {:layer 1} AtomicInit ({:linear "tid"} xls:[X]bool) +modifies cs, t, s, T; +{ assert xls == MapConstBool(true); cs := nil; t := 0; s := 0; T := RightOpen(0); } + +procedure {:yields} {:layer 0} {:refines "AtomicInit"} Init ({:linear "tid"} xls:[X]bool); + +procedure {:atomic} {:layer 1} AtomicGetTicket ({:linear "tid"} tid: X) returns (m: int) +modifies t, T; +{ m := t; t := t + 1; T[m] := true; } + +procedure {:yields} {:layer 0} {:refines "AtomicGetTicket"} GetTicket ({:linear "tid"} tid: X) returns (m: int); -procedure {:yields} {:layer 0,1} GetTicket({:linear "tid"} tid: X) returns (m: int); -ensures {:atomic} |{ A: m := t; t := t + 1; T[m] := true; return true; }|; +procedure {:atomic} {:layer 1,2} AtomicWaitAndEnter ({:linear "tid"} tid: X, m:int) +modifies cs; +{ assume m == s; cs := tid; } -procedure {:yields} {:layer 0,2} WaitAndEnter({:linear "tid"} tid: X, m:int); -ensures {:atomic} |{ A: assume m <= s; cs := tid; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicWaitAndEnter"} WaitAndEnter ({:linear "tid"} tid: X, m:int); -procedure {:yields} {:layer 0,2} Leave({:linear "tid"} tid: X); -ensures {:atomic} |{ A: assert cs == tid; s := s + 1; cs := nil; return true; }|; +procedure {:atomic} {:layer 1,2} AtomicLeave ({:linear "tid"} tid: X) +modifies cs, s; +{ assert cs == tid; s := s + 1; cs := nil; } -procedure {:yields} {:layer 0,2} AllocateLow({:linear_in "tid"} xls':[X]bool) returns ({:linear "tid"} xls: [X]bool, {:linear "tid"} xl: X); -ensures {:atomic} |{ A: assume xl != nil; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicLeave"} Leave ({:linear "tid"} tid: X); + +procedure {:atomic} {:layer 1,2} AtomicAllocateLow ({:linear_in "tid"} xls':[X]bool) returns ({:linear "tid"} xls: [X]bool, {:linear "tid"} xl: X) +{ assume xl != nil && xls'[xl]; xls := xls'[xl := false]; } + +procedure {:yields} {:layer 0} {:refines "AtomicAllocateLow"} AllocateLow ({:linear_in "tid"} xls':[X]bool) returns ({:linear "tid"} xls: [X]bool, {:linear "tid"} xl: X); + +// ########################################################################### +// Collectors for linear domains + +function {:builtin "MapConst"} MapConstBool (bool) : [X]bool; +function {:inline} {:linear "tid"} TidCollector (x: X) : [X]bool +{ + MapConstBool(false)[x := true] +} +function {:inline} {:linear "tid"} TidSetCollector (x: [X]bool) : [X]bool +{ + x +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ticket.bpl.expect boogie-2.4.1+dfsg/Test/civl/ticket.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/ticket.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/ticket.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 26 verified, 0 errors +Boogie program verifier finished with 36 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/treiber-stack.bpl boogie-2.4.1+dfsg/Test/civl/treiber-stack.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/treiber-stack.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/treiber-stack.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -16,30 +16,63 @@ function Remove(x: lmap, i: int): (lmap); axiom (forall x: lmap, i: int :: dom(Remove(x, i)) == dom(x)[i:=false] && map(Remove(x, i)) == map(x)); -procedure {:yields} {:layer 0,1} ReadTopOfStack() returns (v:int); -ensures {:right} |{ A: assume v == null || dom(Stack)[v] || Used[v]; return true; }|; +procedure {:right} {:layer 1} AtomicReadTopOfStack() returns (v:int) +{ assume v == null || dom(Stack)[v] || Used[v]; } -procedure {:yields} {:layer 0,1} Load(i:int) returns (v:int); -ensures {:right} |{ A: assert dom(Stack)[i] || Used[i]; goto B,C; - B: assume dom(Stack)[i]; v := map(Stack)[i]; return true; - C: assume !dom(Stack)[i]; return true; }|; - -procedure {:yields} {:layer 0,1} Store({:linear_in "Node"} l_in:lmap, i:int, v:int) returns ({:linear "Node"} l_out:lmap); -ensures {:both} |{ A: assert dom(l_in)[i]; l_out := Add(l_in, i, v); return true; }|; - -procedure {:yields} {:layer 0,1} TransferToStack(oldVal: int, newVal: int, {:linear_in "Node"} l_in:lmap) returns (r: bool, {:linear "Node"} l_out:lmap); -ensures {:atomic} |{ A: assert dom(l_in)[newVal]; - goto B,C; - B: assume oldVal == TopOfStack; TopOfStack := newVal; l_out := EmptyLmap(); Stack := Add(Stack, newVal, map(l_in)[newVal]); r := true; return true; - C: assume oldVal != TopOfStack; l_out := l_in; r := false; return true; }|; - -procedure {:yields} {:layer 0,1} TransferFromStack(oldVal: int, newVal: int) returns (r: bool); -ensures {:atomic} |{ A: goto B,C; - B: assume oldVal == TopOfStack; TopOfStack := newVal; Used[oldVal] := true; Stack := Remove(Stack, oldVal); r := true; return true; - C: assume oldVal != TopOfStack; r := false; return true; }|; +procedure {:yields} {:layer 0} {:refines "AtomicReadTopOfStack"} ReadTopOfStack() returns (v:int); -var {:layer 0} TopOfStack: int; -var {:linear "Node"} {:layer 0} Stack: lmap; +procedure {:right} {:layer 1} AtomicLoad(i:int) returns (v:int) +{ + assert dom(Stack)[i] || Used[i]; + if (dom(Stack)[i]) { + v := map(Stack)[i]; + } +} + +procedure {:yields} {:layer 0} {:refines "AtomicLoad"} Load(i:int) returns (v:int); + +procedure {:both} {:layer 1} AtomicStore({:linear_in "Node"} l_in:lmap, i:int, v:int) returns ({:linear "Node"} l_out:lmap) +{ assert dom(l_in)[i]; l_out := Add(l_in, i, v); } + +procedure {:yields} {:layer 0} {:refines "AtomicStore"} Store({:linear_in "Node"} l_in:lmap, i:int, v:int) returns ({:linear "Node"} l_out:lmap); + +procedure {:atomic} {:layer 1} AtomicTransferToStack(oldVal: int, newVal: int, {:linear_in "Node"} l_in:lmap) returns (r: bool, {:linear "Node"} l_out:lmap) +modifies TopOfStack, Stack; +{ + assert dom(l_in)[newVal]; + if (oldVal == TopOfStack) { + TopOfStack := newVal; + l_out := EmptyLmap(); + Stack := Add(Stack, newVal, map(l_in)[newVal]); + r := true; + } else { + l_out := l_in; + r := false; + } +} + +procedure {:yields} {:layer 0} {:refines "AtomicTransferToStack"} TransferToStack(oldVal: int, newVal: int, {:linear_in "Node"} l_in:lmap) returns (r: bool, {:linear "Node"} l_out:lmap); + +procedure {:atomic} {:layer 1} AtomicTransferFromStack(oldVal: int, newVal: int) returns (r: bool) +modifies TopOfStack, Used, Stack; +{ + assert oldVal != null; + assert Inv(TopOfStack, Stack); + if (oldVal == TopOfStack) { + TopOfStack := newVal; + Used[oldVal] := true; + Stack := Remove(Stack, oldVal); + r := true; + } + else { + r := false; + } +} + +procedure {:yields} {:layer 0} {:refines "AtomicTransferFromStack"} TransferFromStack(oldVal: int, newVal: int) returns (r: bool); + +var {:layer 0,2} TopOfStack: int; +var {:linear "Node"} {:layer 0,2} Stack: lmap; function {:inline} Inv(TopOfStack: int, Stack: lmap) : (bool) @@ -48,7 +81,7 @@ Subset(BetweenSet(map(Stack), TopOfStack, null), Union(Singleton(null), dom(Stack))) } -var {:linear "Node"} {:layer 0} Used: [int]bool; +var {:linear "Node"} {:layer 0,2} Used: [int]bool; function {:inline} {:linear "Node"} NodeCollector(x: int) : [int]bool { @@ -59,11 +92,14 @@ x } -procedure {:yields} {:layer 1} push(x: int, {:linear_in "Node"} x_lmap: lmap) +procedure {:atomic} {:layer 2} atomic_push(x: int, {:linear_in "Node"} x_lmap: lmap) +modifies Stack, TopOfStack; +{ assert dom(x_lmap)[x]; Stack := Add(Stack, x, TopOfStack); TopOfStack := x; } + +procedure {:yields} {:layer 1} {:refines "atomic_push"} push(x: int, {:linear_in "Node"} x_lmap: lmap) requires {:layer 1} dom(x_lmap)[x]; requires {:layer 1} Inv(TopOfStack, Stack); ensures {:layer 1} Inv(TopOfStack, Stack); -ensures {:atomic} |{ A: Stack := Add(Stack, x, TopOfStack); TopOfStack := x; return true; }|; { var t: int; var g: bool; @@ -78,22 +114,25 @@ { call t := ReadTopOfStack(); call t_lmap := Store(t_lmap, x, t); - call g, t_lmap := TransferToStack(t, x, t_lmap); + call g, t_lmap := TransferToStack(t, x, t_lmap); if (g) { break; } - yield; + yield; assert {:layer 1} dom(t_lmap) == dom(x_lmap); assert {:layer 1} Inv(TopOfStack, Stack); } - yield; + yield; assert {:expand} {:layer 1} Inv(TopOfStack, Stack); } -procedure {:yields} {:layer 1} pop() returns (t: int) +procedure {:atomic} {:layer 2} atomic_pop() returns (t: int) +modifies Used, TopOfStack, Stack; +{ assert Inv(TopOfStack, Stack); assume TopOfStack != null; t := TopOfStack; Used[t] := true; TopOfStack := map(Stack)[t]; Stack := Remove(Stack, t); } + +procedure {:yields} {:layer 1} {:refines "atomic_pop"} pop() returns (t: int) requires {:layer 1} Inv(TopOfStack, Stack); ensures {:layer 1} Inv(TopOfStack, Stack); -ensures {:atomic} |{ A: assume TopOfStack != null; t := TopOfStack; Used[t] := true; TopOfStack := map(Stack)[t]; Stack := Remove(Stack, t); return true; }|; { var g: bool; var x: int; @@ -106,8 +145,8 @@ call t := ReadTopOfStack(); if (t != null) { call x := Load(t); - call g := TransferFromStack(t, x); - if (g) { + call g := TransferFromStack(t, x); + if (g) { break; } } @@ -138,7 +177,7 @@ //////////////////// // Between predicate -//////////////////// +//////////////////// function Between(f: [int]int, x: int, y: int, z: int) returns (bool); function Avoiding(f: [int]int, x: int, y: int, z: int) returns (bool); @@ -165,7 +204,7 @@ axiom(forall f: [int]int, x: int :: Between(f, x, x, x)); // step -axiom(forall f: [int]int, x: int, y: int, z: int, w:int :: {Between(f, y, z, w), f[x]} Between(f, x, f[x], f[x])); +axiom(forall f: [int]int, x: int, y: int, z: int, w:int :: {Between(f, y, z, w), f[x]} Between(f, x, f[x], f[x])); // reach axiom(forall f: [int]int, x: int, y: int :: {f[x], Between(f, x, y, y)} Between(f, x, y, y) ==> x == y || Between(f, x, f[x], y)); @@ -191,7 +230,7 @@ // transitive3 axiom(forall f: [int]int, x: int, y: int, z: int, w: int :: {Between(f, x, y, z), Between(f, x, w, y)} Between(f, x, y, z) && Between(f, x, w, y) ==> Between(f, x, w, z) && Between(f, w, y, z)); -// This axiom is required to deal with the incompleteness of the trigger for the reflexive axiom. +// This axiom is required to deal with the incompleteness of the trigger for the reflexive axiom. // It cannot be proved using the rest of the axioms. axiom(forall f: [int]int, u:int, x: int :: {Between(f, u, x, x)} Between(f, u, x, x) ==> Between(f, u, u, x)); @@ -204,4 +243,4 @@ axiom (forall f: [int]int, p: int, q: int, u: int, w: int :: {BetweenSet(f[p := q], u, w)} Avoiding(f, u, w, p) ==> Equal(BetweenSet(f[p := q], u, w), BetweenSet(f, u, w))); axiom (forall f: [int]int, p: int, q: int, u: int, w: int :: {BetweenSet(f[p := q], u, w)} p != w && Avoiding(f, u, p, w) && Avoiding(f, q, w, p) ==> Equal(BetweenSet(f[p := q], u, w), Union(BetweenSet(f, u, p), BetweenSet(f, q, w)))); -axiom (forall f: [int]int, p: int, q: int, u: int, w: int :: {BetweenSet(f[p := q], u, w)} Avoiding(f, u, w, p) || (p != w && Avoiding(f, u, p, w) && Avoiding(f, q, w, p)) || Equal(BetweenSet(f[p := q], u, w), Empty())); \ No newline at end of file +axiom (forall f: [int]int, p: int, q: int, u: int, w: int :: {BetweenSet(f[p := q], u, w)} Avoiding(f, u, w, p) || (p != w && Avoiding(f, u, p, w) && Avoiding(f, q, w, p)) || Equal(BetweenSet(f[p := q], u, w), Empty())); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/treiber-stack.bpl.expect boogie-2.4.1+dfsg/Test/civl/treiber-stack.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/treiber-stack.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/treiber-stack.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 8 verified, 0 errors +Boogie program verifier finished with 13 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/unprotected-read.bpl boogie-2.4.1+dfsg/Test/civl/unprotected-read.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/unprotected-read.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/unprotected-read.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,39 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// Write (although lock-protected) is a non-mover, becaues of the unprotected +// read action ReadNoLock. + +type Tid; +const nil:Tid; + +var {:layer 0,1} lock:Tid; +var {:layer 0,1} x:int; + +//////////////////////////////////////////////////////////////////////////////// + +procedure {:right} {:layer 1} Acquire({:linear "tid"} tid:Tid) +modifies lock; +{ assert tid != nil; assume lock == nil; lock := tid; } + +procedure {:left} {:layer 1} Release({:linear "tid"} tid:Tid) +modifies lock; +{ assert tid != nil && lock == tid; lock := nil; } + +procedure {:atomic} {:layer 1} Write({:linear "tid"} tid:Tid, val:int) +modifies x; +{ assert tid != nil && lock == tid; x := val; } + +procedure {:both} {:layer 1} ReadLock({:linear "tid"} tid:Tid) returns (val:int) +{ assert tid != nil && lock == tid; val := x; } + +procedure {:atomic} {:layer 1} ReadNoLock() returns (val:int) +{ val := x; } + +//////////////////////////////////////////////////////////////////////////////// + +function {:builtin "MapConst"} MapConstBool(bool) : [Tid]bool; +function {:inline} {:linear "tid"} TidCollector(tid:Tid) : [Tid]bool +{ + MapConstBool(false)[tid := true] +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/unprotected-read.bpl.expect boogie-2.4.1+dfsg/Test/civl/unprotected-read.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/unprotected-read.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/unprotected-read.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 17 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/verified-ft.bpl boogie-2.4.1+dfsg/Test/civl/verified-ft.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/verified-ft.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/verified-ft.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,1227 @@ +/* + +Copyright (c) 2017, Cormac Flanagan (University of California, Santa Cruz) + Stephen Freund (Williams College) + James Wilcox (University of Washington) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the names of the University of California, Santa Cruz + and Williams College nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + * Proof of VerifiedFT correctness in CIVL. + */ + +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +/* + * Tid + */ +type Tid = int; // make int so you can iterate over Tids +const unique nil: Tid; + +function {:inline} ValidTid(tid : Tid): bool { + tid != nil && tid >= 0 +} + +/* + * datatype Epoch = Tid*Clock + */ +type{:datatype} Epoch; +function{:constructor} epoch(tid:Tid, clock:int):Epoch; + +const unique SHARED: Epoch; + +function {:inline} EpochInc(e : Epoch): Epoch { + epoch(tid#epoch(e),clock#epoch(e) + 1) +} + +function {:inline} EpochIsShared(e : Epoch): bool { + e == SHARED +} + +function {:inline} EpochLeq(e1 : Epoch, e2 : Epoch): bool { + tid#epoch(e1) == tid#epoch(e2) && clock#epoch(e1) <= clock#epoch(e2) +} + +function {:inline} max(a : int, b : int) : int { + if (a < b) then b else a +} + +function {:inline} EpochMax(e1 : Epoch, e2 : Epoch): Epoch { + epoch(tid#epoch(e1), max(clock#epoch(e1), clock#epoch(e2))) +} + +function {:inline} EpochInit(tid: Tid): Epoch { + epoch(tid, 0) +} + + +/* + * VC + */ +type VC = [Tid]Epoch; + +// primite accessors to array +// len of VC is stored at -1. +function {:inline} VCArrayLen(vc: VC): int { clock#epoch(vc[-1]) } +function {:inline} VCArraySetLen(vc: VC, n: int): VC { vc[-1 := epoch(-1,n)] } +function {:inline} VCArrayGet(vc: VC, i: int): Epoch { vc[i] } + + +/* + * Shadow State + */ +type Lock; +type Var; + +/* + * datatype Shadowable = tid | lock | var + */ +type{:datatype} Shadowable; +function {:constructor} ShadowableTid(tid: Tid): Shadowable; +function {:constructor} ShadowableLock(l: Lock): Shadowable; +function {:constructor} ShadowableVar(x: Var): Shadowable; + +var {:layer 0,30} shadow.VC : [Shadowable] VC; +var {:layer 0,30} shadow.Lock : [Shadowable] Tid; +var {:layer 0,30} sx.W : [Var]Epoch; +var {:layer 0,30} sx.R : [Var]Epoch; + +/* + * Trace Invariant Support + */ +type ThreadStatus = int; +function{:inline} UNUSED(): ThreadStatus { 0 } +function{:inline} NEW(): ThreadStatus { 1 } +function{:inline} RUNNING(): ThreadStatus { 2 } +function{:inline} STOPPED(): ThreadStatus { 3 } + +var {:layer 0,30} thread.State: [Tid]ThreadStatus; // status of each thread +var {:layer 0,30} thread.ForkedBy: [Tid]Tid; // if forkedBy[t] = u, the u started t +var {:layer 0,30} thread.HasJoined: [Tid,Tid]bool; // if hasJoined[t,u], then t joined u. + + +/* + * Needed for [Read Share] + */ +const unique EMPTY_MAP: [Tid]Epoch; +axiom (forall i : Tid :: EMPTY_MAP[i] == EpochInit(i)); + +function {:inline} VC.bottom(): VC { VCArraySetLen(EMPTY_MAP,0) } + + +/* + * State Invariants + */ +function {:inline false} VarsRepOk(w: [Var]Epoch, r: [Var]Epoch ): bool { + (forall v: Var :: ValidTid(tid#epoch(w[v]))) && + (forall v: Var :: r[v] == SHARED || (tid#epoch(r[v]) >= 0 && tid#epoch(r[v]) != nil)) +} + +function {:inline false} VCRepOk(vc: VC): bool { + VCArrayLen(vc) >= 0 && + (forall j: int :: {vc[j]} 0 <= j && j < VCArrayLen(vc) ==> clock#epoch(vc[j]) >= 0) && + (forall j: int :: {vc[j]} 0 <= j && j < VCArrayLen(vc) ==> tid#epoch(vc[j]) == j) && + (forall j: int :: VCArrayLen(vc) <= j ==> vc[j] == EpochInit(j)) +} + +function {:inline} VCsRepOk(vcs: [Shadowable]VC): bool { + (forall s : Shadowable :: VCRepOk(vcs[s])) +} + +function {:inline} FTRepOk(vcs: [Shadowable]VC, w: [Var]Epoch, r: [Var]Epoch): bool { + VCsRepOk(vcs) && + VarsRepOk(w, r) +} + + +/* + * Environment Assumptions -- what's preserved across yields + */ +function {:inline} LocksPreserved({:linear "tid" } tid: Tid, oldLocks: [Shadowable] Tid, locks: [Shadowable]Tid): bool { + (forall v: Shadowable :: oldLocks[v] == tid ==> locks[v] == tid) +} + +function {:inline} SharedInvPreserved(oldR: [Var] Epoch, r: [Var] Epoch): bool { + (forall x: Var :: oldR[x] == SHARED ==> r[x] == SHARED) +} + +function {:inline} FTPreserved({:linear "tid" } tid:Tid, + oldLocks: [Shadowable] Tid, oldVcs: [Shadowable]VC, oldW: [Var]Epoch, oldR: [Var]Epoch, + locks: [Shadowable] Tid, vcs: [Shadowable]VC, w: [Var]Epoch, r: [Var]Epoch): bool { + LocksPreserved(tid, oldLocks, locks) && + SharedInvPreserved(oldR, r) && + (forall s: Shadowable :: oldLocks[s] == tid ==> vcs[s] == oldVcs[s]) && + (forall x: Var :: oldLocks[ShadowableVar(x)] == tid ==> r[x] == oldR[x]) && + (forall x: Var :: oldLocks[ShadowableVar(x)] == tid ==> w[x] == oldW[x]) +} + +/****** Layer 0 ******/ + +// VarState Lock +procedure {:yields} {:layer 0} {:refines "AtomicAcquireVarLock"} AcquireVarLock({:linear "tid"} tid: Tid, x : Var); +procedure {:right} {:layer 1,20} AtomicAcquireVarLock({:linear "tid"} tid: Tid, x : Var) +modifies shadow.Lock; +{ assert ValidTid(tid); assume shadow.Lock[ShadowableVar(x)] == nil; shadow.Lock[ShadowableVar(x)] := tid; } + +procedure {:yields} {:layer 0} {:refines "AtomicReleaseVarLock"} ReleaseVarLock({:linear "tid"} tid: Tid, x : Var); +procedure {:left} {:layer 1,20} AtomicReleaseVarLock({:linear "tid"} tid: Tid, x : Var) +modifies shadow.Lock; +{ assert ValidTid(tid); assert shadow.Lock[ShadowableVar(x)] == tid; shadow.Lock[ShadowableVar(x)] := nil; } + + +// ThreadState +procedure {:yields} {:layer 0} {:refines "AtomicThreadStateGetE"} ThreadStateGetE({:linear "tid"} tid: Tid) returns (e:Epoch); +procedure {:both} {:layer 1,20} AtomicThreadStateGetE({:linear "tid"} tid: Tid) returns (e:Epoch) +{ assert ValidTid(tid); assert shadow.Lock[ShadowableTid(tid)] == tid; e := shadow.VC[ShadowableTid(tid)][tid]; } + + +// VarState +procedure {:yields} {:layer 0} {:refines "AtomicVarStateSetW"} VarStateSetW({:linear "tid"} tid:Tid, x : Var, e:Epoch); +procedure {:atomic} {:layer 1,20} AtomicVarStateSetW({:linear "tid"} tid:Tid, x : Var, e:Epoch) +modifies sx.W; +{ assert ValidTid(tid); assert shadow.Lock[ShadowableVar(x)] == tid; sx.W[x] := e; } + +procedure {:yields} {:layer 0} {:refines "AtomicVarStateGetW"} VarStateGetW({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch); +procedure {:both} {:layer 1,20} AtomicVarStateGetW({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch) +{ assert ValidTid(tid); assert shadow.Lock[ShadowableVar(x)] == tid; e := sx.W[x]; } + +procedure {:yields} {:layer 0} {:refines "AtomicVarStateGetWNoLock"} VarStateGetWNoLock({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch); +procedure {:atomic} {:layer 1,20} AtomicVarStateGetWNoLock({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch) +{ assert ValidTid(tid); e := sx.W[x]; } + +procedure {:yields} {:layer 0} {:refines "AtomicVarStateSetR"} VarStateSetR({:linear "tid"} tid:Tid, x : Var, e:Epoch); +procedure {:atomic} {:layer 1,20} AtomicVarStateSetR({:linear "tid"} tid:Tid, x : Var, e:Epoch) +modifies sx.R; +{ assert ValidTid(tid); assert shadow.Lock[ShadowableVar(x)] == tid; assert sx.R[x] != SHARED; sx.R[x] := e; } + +procedure {:yields} {:layer 0} {:refines "AtomicVarStateGetRNoLock"} VarStateGetRNoLock({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch); +procedure {:atomic} {:layer 1,20} AtomicVarStateGetRNoLock({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch) +{ assert ValidTid(tid); e := sx.R[x]; } + +procedure {:yields} {:layer 0} {:refines "AtomicVarStateGetR"} VarStateGetR({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch); +procedure {:both} {:layer 1,20} AtomicVarStateGetR({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch) +{ assert ValidTid(tid); assert shadow.Lock[ShadowableVar(x)] == tid; e := sx.R[x]; } + +procedure {:yields} {:layer 0} {:refines "AtomicVarStateGetRShared"} VarStateGetRShared({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch); +procedure {:right} {:layer 1,20} AtomicVarStateGetRShared({:linear "tid"} tid:Tid, x : Var) returns (e:Epoch) +{ assert ValidTid(tid); assume sx.R[x] == SHARED; e := SHARED; } + + +// VCs + +procedure {:yields} {:layer 0} {:refines "AtomicVCGetSize"} VCGetSize({:linear "tid" } tid: Tid, r: Shadowable) returns (i: int); +procedure {:both} {:layer 1,10} AtomicVCGetSize({:linear "tid" } tid: Tid, r: Shadowable) returns (i: int) +{ + assert ValidTid(tid); + assert (shadow.Lock[r] == tid); + i := VCArrayLen(shadow.VC[r]); +} + +procedure {:yields} {:layer 0} {:refines "AtomicVCGetElem"} VCGetElem({:linear "tid" } tid: Tid, r: Shadowable, i: int) returns (e: Epoch); +procedure {:both} {:layer 1,20} AtomicVCGetElem({:linear "tid" } tid: Tid, r: Shadowable, i: int) returns (e: Epoch) +{ + assert ValidTid(tid); + assert (shadow.Lock[r] == tid); + e := VCArrayGet(shadow.VC[r], i); +} + +procedure {:yields} {:layer 0} {:refines "AtomicVCGetElemShared"} VCGetElemShared({:linear "tid" } tid: Tid, x : Var) returns (e: Epoch); +procedure {:atomic} {:layer 1,20} AtomicVCGetElemShared({:linear "tid" } tid: Tid, x : Var) returns (e: Epoch) +{ + assert sx.R[x] == SHARED; + assert ValidTid(tid); + e := VCArrayGet(shadow.VC[ShadowableVar(x)], tid); +} + +procedure {:yields} {:layer 0} {:refines "AtomicVCSetElemShared"} VCSetElemShared({:linear "tid" } tid: Tid, x : Var, e: Epoch); +procedure {:both} {:layer 1,20} AtomicVCSetElemShared({:linear "tid" } tid: Tid, x : Var, e: Epoch) +modifies shadow.VC; +{ + assert sx.R[x] == SHARED; + assert ValidTid(tid); + assert (shadow.Lock[ShadowableVar(x)] == tid); + shadow.VC[ShadowableVar(x)][tid] := e; + shadow.VC[ShadowableVar(x)] := VCArraySetLen(shadow.VC[ShadowableVar(x)], max(VCArrayLen(shadow.VC[ShadowableVar(x)]),tid+1)); +} + +procedure {:yields} {:layer 0} {:refines "AtomicVCSetElem"} VCSetElem({:linear "tid" } tid: Tid, r: Shadowable, i: int, e: Epoch); +procedure {:both} {:layer 1,20} AtomicVCSetElem({:linear "tid" } tid: Tid, r: Shadowable, i: int, e: Epoch) +modifies shadow.VC; +{ + assert is#ShadowableVar(r) ==> sx.R[x#ShadowableVar(r)] != SHARED; + assert ValidTid(tid); + assert (shadow.Lock[r] == tid); + shadow.VC[r][i] := e; + shadow.VC[r] := VCArraySetLen(shadow.VC[r], max(VCArrayLen(shadow.VC[r]),i+1)); +} + +procedure {:yields} {:layer 0} {:refines "AtomicVCInit"} VCInit({:linear "tid" } tid: Tid, r: Shadowable); +procedure {:both} {:layer 1,20} AtomicVCInit({:linear "tid" } tid: Tid, r: Shadowable) +modifies shadow.VC; +{ + assert ValidTid(tid); + assert is#ShadowableVar(r) ==> sx.R[x#ShadowableVar(r)] != SHARED; + assert (shadow.Lock[r] == tid); + shadow.VC[r] := VC.bottom(); +} + + +/****** Layer 10 -> 20 ******/ + + procedure {:yields} {:layer 10} Yield10({:linear "tid"} tid:Tid) + requires {:layer 10} ValidTid(tid); + requires {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + ensures {:layer 10} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10} FTPreserved(tid, old(shadow.Lock), old(shadow.VC), old(sx.W), old(sx.R), shadow.Lock, shadow.VC, sx.W, sx.R); + ensures {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + { + yield; + assert {:layer 10} ValidTid(tid); + assert {:layer 10} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + assert {:layer 10} FTPreserved(tid, old(shadow.Lock), old(shadow.VC), old(sx.W), old(sx.R), shadow.Lock, shadow.VC, sx.W, sx.R); + assert {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + } + +procedure {:both} {:layer 11,20} AtomicVC.Leq({:linear "tid"} tid: Tid, v1: Shadowable, v2: Shadowable) returns (res: bool) +{ + assert ValidTid(tid); + assert shadow.Lock[v1] == tid; + assert shadow.Lock[v2] == tid; + assert shadow.Lock[ShadowableTid(tid)] == tid; + assert is#ShadowableVar(v1) ==> sx.R[x#ShadowableVar(v1)] == SHARED; + assert !is#ShadowableVar(v2); + res := (forall j : int :: {f(j)} 0 <= j && f(j) ==> EpochLeq(VCArrayGet(shadow.VC[v1], j), VCArrayGet(shadow.VC[v2], j))); +} + +procedure {:yields} {:layer 10} {:refines "AtomicVC.Leq"} VC.Leq({:linear "tid"} tid: Tid, v1: Shadowable, v2: Shadowable) returns (res: bool) + requires {:layer 10} ValidTid(tid); + requires {:layer 10} shadow.Lock[v1] == tid; + requires {:layer 10} shadow.Lock[v2] == tid; + requires {:layer 10} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + requires {:layer 10} is#ShadowableVar(v1) ==> sx.R[x#ShadowableVar(v1)] == SHARED; + requires {:layer 10} !is#ShadowableVar(v2); + + ensures {:layer 10} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10} FTPreserved(tid, old(shadow.Lock), old(shadow.VC), old(sx.W), old(sx.R), shadow.Lock, shadow.VC, sx.W, sx.R); + ensures {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); +{ + var vc1, vc2: VC; + var len1, len2 : int; + var e1, e2: Epoch; + var i: int; + + call Yield10(tid); + + call len1 := VCGetSize(tid, v1); + call len2 := VCGetSize(tid, v1); + + i := 0; + while (i < max(len1, len2)) + invariant {:layer 10} 0 <= i; + invariant {:layer 10} (forall j : int :: {f(j)} + 0 <= j && j < i && f(j) ==> + EpochLeq(VCArrayGet(shadow.VC[v1], j), VCArrayGet(shadow.VC[v2], j))); + { + call e1 := VCGetElem(tid, v1, i); + call e2 := VCGetElem(tid, v2, i); + if (!EpochLeq(e1, e2)) { + assert {:layer 10} f(i); + res := false; + call Yield10(tid); + return; + } + + i := i + 1; + } + + res := true; + call Yield10(tid); + return; +} + +procedure {:both} {:layer 11,20} AtomicVC.Copy({:linear "tid"} tid: Tid, v1: Shadowable, v2: Shadowable) +modifies shadow.VC; +{ + assert ValidTid(tid); + assert v1 != v2; + assert shadow.Lock[ShadowableTid(tid)] == tid; + assert shadow.Lock[v1] == tid; + assert shadow.Lock[v2] == tid; + assert !is#ShadowableVar(v1); + assert !is#ShadowableVar(v2); + assert VCRepOk(shadow.VC[v2]); + assert VCRepOk(shadow.VC[v1]); + if (*) { + havoc shadow.VC; + assume VCRepOk(shadow.VC[v1]); + assume VCArrayLen(shadow.VC[v1]) == max(VCArrayLen(old(shadow.VC)[v1]),VCArrayLen(old(shadow.VC)[v2])); + assume (forall j: int :: 0 <= j ==> VCArrayGet(shadow.VC[v1], j) == VCArrayGet(old(shadow.VC)[v2], j)); + assume shadow.VC == old(shadow.VC)[v1 := shadow.VC[v1]]; + } else { + shadow.VC[v1] := shadow.VC[v2]; + } +} + +procedure {:yields} {:layer 10} {:refines "AtomicVC.Copy"} VC.Copy({:linear "tid"} tid: Tid, v1: Shadowable, v2: Shadowable) + requires {:layer 10} ValidTid(tid); + requires {:layer 10} v1 != v2; + requires {:layer 10} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10} shadow.Lock[v1] == tid; + requires {:layer 10} shadow.Lock[v2] == tid; + requires {:layer 10} !is#ShadowableVar(v1); + requires {:layer 10} !is#ShadowableVar(v2); + requires {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 10} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + ensures {:layer 10} (forall s: Shadowable :: s != v1 && old(shadow.Lock)[s] == tid ==> old(shadow.VC)[s] == shadow.VC[s]); +{ + var len1, len2 : int; + var e1, e2: Epoch; + var i: int; + + var {:layer 10} oldVC : [Shadowable] [Tid]Epoch; + var {:layer 10} oldLock : [Shadowable] Tid; + + call Yield10(tid); + + call oldLock, oldVC := GhostRead(); + + call len1 := VCGetSize(tid, v1); + call len2 := VCGetSize(tid, v2); + + i := 0; + while (i < max(len1, len2)) + invariant {:layer 10} (forall s: Shadowable :: s != v1 ==> shadow.VC[s] == oldVC[s]); + invariant {:layer 10} (forall s: Shadowable :: oldLock[s] == tid ==> shadow.Lock[s] == tid); + invariant {:layer 10} VCRepOk(shadow.VC[v1]); + invariant {:layer 10} i >= 0; + invariant {:layer 10} i <= max(len1, len2); + invariant {:layer 10} VCArrayLen(shadow.VC[v1]) == max(len1, i); + invariant {:layer 10} (forall j : int :: 0 <= j && j < i ==> VCArrayGet(shadow.VC[v1], j) == VCArrayGet(shadow.VC[v2], j)); + invariant {:layer 10} (forall j : int :: max(len1, len2) <=j ==> VCArrayGet(shadow.VC[v1], j) == VCArrayGet(shadow.VC[v2], j)); + { + call e2 := VCGetElem(tid, v2, i); + call VCSetElem(tid, v1, i, e2); + i := i + 1; + } + + call Yield10(tid); +} + + +procedure {:right} {:layer 11,20} AtomicVC.Join({:linear "tid"} tid: Tid, v1: Shadowable, v2: Shadowable) +modifies shadow.VC; +{ + assert ValidTid(tid); + assert v1 != v2; + assert shadow.Lock[ShadowableTid(tid)] == tid; + assert shadow.Lock[v1] == tid; + assert shadow.Lock[v2] == tid; + assert !is#ShadowableVar(v1); + assert !is#ShadowableVar(v2); + assert VCRepOk(shadow.VC[v2]); + havoc shadow.VC; + assume VCRepOk(shadow.VC[v1]); + assume VCArrayLen(shadow.VC[v1]) == max(VCArrayLen(old(shadow.VC)[v1]),VCArrayLen(old(shadow.VC)[v2])); + assume (forall j: int :: 0 <= j ==> VCArrayGet(shadow.VC[v1], j) == EpochMax(VCArrayGet(old(shadow.VC)[v1], j), VCArrayGet(old(shadow.VC)[v2], j))); + assume shadow.VC == old(shadow.VC)[v1 := shadow.VC[v1]]; +} + +procedure {:yields} {:layer 10} {:refines "AtomicVC.Join"} VC.Join({:linear "tid"} tid: Tid, v1: Shadowable, v2: Shadowable) + requires {:layer 10} ValidTid(tid); + requires {:layer 10} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10} shadow.Lock[v1] == tid; + requires {:layer 10} shadow.Lock[v2] == tid; + requires {:layer 10} !is#ShadowableVar(v1); + requires {:layer 10} !is#ShadowableVar(v2); + requires {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 10} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + ensures {:layer 10} (forall s: Shadowable :: s != v1 && old(shadow.Lock)[s] == tid ==> old(shadow.VC)[s] == shadow.VC[s]); +{ + var len1, len2 : int; + var e1, e2: Epoch; + var i: int; + + var {:layer 10} oldVC : [Shadowable] [Tid]Epoch; + var {:layer 10} oldLock : [Shadowable] Tid; + + call Yield10(tid); + + call oldLock, oldVC := GhostRead(); + + call len1 := VCGetSize(tid, v1); + call len2 := VCGetSize(tid, v2); + + i := 0; + while (i < max(len1, len2)) + invariant {:layer 10} (forall s: Shadowable :: s != v1 ==> shadow.VC[s] == oldVC[s]); + invariant {:layer 10} (forall s: Shadowable :: oldLock[s] == tid ==> shadow.Lock[s] == tid); + invariant {:layer 10} VCRepOk(shadow.VC[v1]); + invariant {:layer 10} i >= 0; + invariant {:layer 10} i <= max(len1, len2); + invariant {:layer 10} VCArrayLen(shadow.VC[v1]) == max(len1, i); + invariant {:layer 10} (forall j : int :: 0 <= j && i <= j ==> VCArrayGet(shadow.VC[v1], j) == VCArrayGet(oldVC[v1], j)); + invariant {:layer 10} (forall j : int :: 0 <= j && j < i ==> VCArrayGet(shadow.VC[v1], j) == EpochMax(VCArrayGet(oldVC[v1], j), VCArrayGet(shadow.VC[v2], j))); + { + call e1 := VCGetElem(tid, v1, i); + call e2 := VCGetElem(tid, v2, i); + call VCSetElem(tid, v1, i, EpochMax(e1, e2)); + i := i + 1; + } + + call Yield10(tid); +} + + +procedure {:layer 10} GhostRead() returns (lock : [Shadowable]Tid, data : [Shadowable] [Tid]Epoch) + ensures lock == shadow.Lock; + ensures data == shadow.VC; +{ + lock := shadow.Lock; + data := shadow.VC; +} + +procedure {:both} {:layer 11,20} AtomicVC.Inc({:linear "tid" } tid: Tid, v: Shadowable, i: int) +modifies shadow.VC; +{ + assert ValidTid(tid); + assert shadow.Lock[ShadowableTid(tid)] == tid; + assert shadow.Lock[v] == tid; + assert !is#ShadowableVar(v); + assert i >= 0; + assert VCRepOk(shadow.VC[v]); + shadow.VC[v] := VCArraySetLen(shadow.VC[v], max(VCArrayLen(shadow.VC[v]), i+1)); + shadow.VC[v] := shadow.VC[v][i := EpochInc(shadow.VC[v][i])]; +} + +procedure {:yields} {:layer 10} {:refines "AtomicVC.Inc"} VC.Inc({:linear "tid" } tid: Tid, v: Shadowable, i: int) + requires {:layer 10} ValidTid(tid); + requires {:layer 10} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10} shadow.Lock[v] == tid; + requires {:layer 10} !is#ShadowableVar(v); + requires {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + requires {:layer 10} VCRepOk(shadow.VC[v]); + requires {:layer 10} i >= 0; + + ensures {:layer 10} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10} FTRepOk(shadow.VC, sx.W, sx.R); + ensures {:layer 10} VCRepOk(shadow.VC[v]); + ensures {:layer 10} (forall s: Shadowable :: s != v && old(shadow.Lock)[s] == tid ==> old(shadow.VC)[s] == shadow.VC[s]); +{ + var e: Epoch; + + call Yield10(tid); + + call e := VCGetElem(tid, v, i); + + call VCSetElem(tid, v, i, EpochInc(e)); + + call Yield10(tid); +} + + + +/****** Layer 20 --> 30 ******/ + + +procedure {:yields} {:layer 20} Yield20({:linear "tid"} tid:Tid) + requires {:layer 10,20} ValidTid(tid); + requires {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 10,20} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10,20} FTPreserved(tid, old(shadow.Lock), old(shadow.VC), old(sx.W), old(sx.R), shadow.Lock, shadow.VC, sx.W, sx.R); + ensures {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); +{ + yield; + assert {:layer 10,20} ValidTid(tid); + assert {:layer 10,20} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + assert {:layer 10,20} FTPreserved(tid, old(shadow.Lock), old(shadow.VC), old(sx.W), old(sx.R), shadow.Lock, shadow.VC, sx.W, sx.R); + assert {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); +} + + +procedure {:atomic} {:layer 21,30} AtomicFork({:linear "tid"} tid:Tid, uid : Tid) +modifies shadow.VC; +{ + var v1,v2: Shadowable; + + assert ValidTid(tid); + assert ValidTid(uid); + assert shadow.Lock[ShadowableTid(tid)] == tid; + assert shadow.Lock[ShadowableTid(uid)] == tid; + assert tid != uid; + + v1 := ShadowableTid(uid); + v2 := ShadowableTid(tid); + + havoc shadow.VC; + + assume VCArrayLen(shadow.VC[v1]) == max(VCArrayLen(old(shadow.VC)[v1]),VCArrayLen(old(shadow.VC)[v2])); + assume VCRepOk(shadow.VC[v1]); + assume (forall j: int :: 0 <= j ==> VCArrayGet(shadow.VC[v1], j) == EpochMax(VCArrayGet(old(shadow.VC)[v1], j), VCArrayGet(old(shadow.VC)[v2], j))); + + assume VCRepOk(shadow.VC[v2]); + assume VCArrayLen(shadow.VC[v2]) == max(VCArrayLen(shadow.VC[v2]), tid+1); + assume (forall j: int :: 0 <= j && j != tid ==> VCArrayGet(shadow.VC[v2], j) == VCArrayGet(old(shadow.VC)[v2], j)); + assume VCArrayGet(shadow.VC[v2], tid) == EpochInc(VCArrayGet(old(shadow.VC)[v2], tid)); + + assume shadow.VC == old(shadow.VC)[v1 := shadow.VC[v1]][v2 := shadow.VC[v2]]; +} + +procedure {:yields} {:layer 20} {:refines "AtomicFork"} Fork({:linear "tid"} tid:Tid, uid : Tid) + requires {:layer 10,20} ValidTid(tid); + requires {:layer 10,20} ValidTid(uid); + requires {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10,20} shadow.Lock[ShadowableTid(uid)] == tid; + requires {:layer 10,20} tid != uid; + requires {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 10,20} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + ensures {:layer 10,20} (forall s: Shadowable :: s != ShadowableTid(tid) && s != ShadowableTid(uid) ==> + (old(shadow.Lock)[s] == tid ==> old(shadow.VC)[s] == shadow.VC[s])); +{ + call Yield20(tid); + + call VC.Join(tid, ShadowableTid(uid), ShadowableTid(tid)); + + call VC.Inc(tid, ShadowableTid(tid), tid); + + call Yield20(tid); +} + + +procedure {:atomic} {:layer 21,30} AtomicJoin({:linear "tid"} tid:Tid, uid : Tid) +modifies shadow.VC; +{ + var v1, v2: Shadowable; + + assert ValidTid(tid); + assert ValidTid(uid); + assert shadow.Lock[ShadowableTid(tid)] == tid; + assert shadow.Lock[ShadowableTid(uid)] == tid; + assert tid != uid; + + v1 := ShadowableTid(uid); + v2 := ShadowableTid(tid); + + havoc shadow.VC; + assume VCArrayLen(shadow.VC[v2]) == max(VCArrayLen(old(shadow.VC)[v1]),VCArrayLen(old(shadow.VC)[v2])); + assume VCRepOk(shadow.VC[v2]); + assume (forall j: int :: 0 <= j ==> + VCArrayGet(shadow.VC[v2], j) == + EpochMax(VCArrayGet(old(shadow.VC)[v2], j), VCArrayGet(old(shadow.VC)[v1], j))); + assume shadow.VC == old(shadow.VC)[v2 := shadow.VC[v2]]; +} + +procedure {:yields} {:layer 20} {:refines "AtomicJoin"} Join({:linear "tid"} tid:Tid, uid : Tid) + requires {:layer 10,20} ValidTid(tid); + requires {:layer 10,20} ValidTid(uid); + requires {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10,20} shadow.Lock[ShadowableTid(uid)] == tid; + requires {:layer 10,20} tid != uid; + requires {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 10,20} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + ensures {:layer 10,20} (forall s: Shadowable :: s != ShadowableTid(tid) && old(shadow.Lock)[s] == tid ==> old(shadow.VC)[s] == shadow.VC[s]); +{ + call Yield20(tid); + call VC.Join(tid, ShadowableTid(tid), ShadowableTid(uid)); + call Yield20(tid); +} + + +procedure {:atomic} {:layer 21,30} AtomicAcquire({:linear "tid"} tid: Tid, l: Lock) +modifies shadow.VC; +{ + var v1, v2: Shadowable; + + assert ValidTid(tid); + assert shadow.Lock[ShadowableLock(l)] == tid; + assert shadow.Lock[ShadowableTid(tid)] == tid; + + v1 := ShadowableTid(tid); + v2 := ShadowableLock(l); + havoc shadow.VC; + assume VCRepOk(shadow.VC[v1]); + assume VCArrayLen(shadow.VC[v1]) == max(VCArrayLen(old(shadow.VC)[v1]),VCArrayLen(old(shadow.VC)[v2])); + assume (forall j: int :: 0 <= j ==> + VCArrayGet(shadow.VC[v1], j) == + EpochMax(VCArrayGet(old(shadow.VC)[v1], j), VCArrayGet(old(shadow.VC)[v2], j))); + assume shadow.VC == old(shadow.VC)[v1 := shadow.VC[v1]]; +} + +procedure {:yields} {:layer 20} {:refines "AtomicAcquire"} Acquire({:linear "tid"} tid: Tid, l: Lock) + requires {:layer 10,20} ValidTid(tid); + requires {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10,20} shadow.Lock[ShadowableLock(l)] == tid; + requires {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 10,20} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + ensures {:layer 10,20} (forall s: Shadowable :: s != ShadowableTid(tid) && old(shadow.Lock)[s] == tid ==> old(shadow.VC)[s] == shadow.VC[s]); +{ + call Yield20(tid); + + call VC.Join(tid, ShadowableTid(tid), ShadowableLock(l)); + + call Yield20(tid); +} + +procedure {:atomic} {:layer 21,30} AtomicRelease({:linear "tid"} tid: Tid, l: Lock) +modifies shadow.VC; +{ + var v1,v2: Shadowable; + + assert ValidTid(tid); + assert shadow.Lock[ShadowableTid(tid)] == tid; + assert shadow.Lock[ShadowableLock(l)] == tid; + + v1 := ShadowableLock(l); + v2 := ShadowableTid(tid); + + havoc shadow.VC; + + // Use the same strategy as in Copy's atomic spec. + if (*) { + assume VCRepOk(shadow.VC[v1]); + assume VCArrayLen(shadow.VC[v1]) == max(VCArrayLen(old(shadow.VC)[v1]),VCArrayLen(old(shadow.VC)[v2])); + assume (forall j: int :: 0 <= j ==> VCArrayGet(shadow.VC[v1], j) == VCArrayGet(old(shadow.VC)[v2], j)); + } else { + assume shadow.VC[v1] == old(shadow.VC)[v2]; + } + + assume VCRepOk(shadow.VC[v2]); + assume VCArrayLen(shadow.VC[v2]) == max(VCArrayLen(shadow.VC[v2]), tid+1); + assume (forall j: int :: 0 <= j && j != tid ==> VCArrayGet(shadow.VC[v2], j) == VCArrayGet(old(shadow.VC)[v2], j)); + assume VCArrayGet(shadow.VC[v2], tid) == EpochInc(VCArrayGet(old(shadow.VC)[v2], tid)); + + assume shadow.VC == old(shadow.VC)[v1 := shadow.VC[v1]][v2 := shadow.VC[v2]]; +} + +procedure {:yields} {:layer 20} {:refines "AtomicRelease"} Release({:linear "tid"} tid: Tid, l: Lock) + requires {:layer 10,20} ValidTid(tid); + requires {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10,20} shadow.Lock[ShadowableLock(l)] == tid; + requires {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 10,20} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + ensures {:layer 10,20} (forall s: Shadowable :: s != ShadowableTid(tid) && s != ShadowableLock(l) && old(shadow.Lock)[s] == tid ==> old(shadow.VC)[s] == shadow.VC[s]); +{ + var sm : Shadowable; + var st : Shadowable; + + call Yield20(tid); + + call VC.Copy(tid, ShadowableLock(l), ShadowableTid(tid)); + + call VC.Inc(tid, ShadowableTid(tid), tid); + + call Yield20(tid); +} + + +procedure {:atomic} {:layer 21,30} AtomicWrite({:linear "tid"} tid:Tid, x : Var) returns (ok : bool) +modifies sx.W; +{ + var st : Shadowable; + var sx : Shadowable; + + assert ValidTid(tid); + st := ShadowableTid(tid); + sx := ShadowableVar(x); + goto WriteFastPath, WriteExclusive, WritedShared, WriteWriteRace, ReadWriteRace, SharedWriteRace; + WriteFastPath: + ok := true; + assume sx.W[x] == VCArrayGet(shadow.VC[st], tid); + return; + WriteExclusive: + ok := true; + assume EpochLeq(sx.W[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.W[x]))); + assume sx.R[x] != SHARED; + assume EpochLeq(sx.R[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.R[x]))); + sx.W[x] := VCArrayGet(shadow.VC[st], tid); + return; + WritedShared: + ok := true; + assume EpochLeq(sx.W[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.W[x]))); + assume sx.R[x] == SHARED; + assume (forall j : int :: {f(j)} + 0 <= j && j < max(VCArrayLen(shadow.VC[sx]), VCArrayLen(shadow.VC[st])) && f(j) ==> + EpochLeq(VCArrayGet(shadow.VC[sx], j), VCArrayGet(shadow.VC[st], j))); + sx.W[x] := VCArrayGet(shadow.VC[st], tid); + return; + WriteWriteRace: + ok := false; + assume !EpochLeq(sx.W[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.W[x]))); + return; + ReadWriteRace: + ok := false; + assume sx.R[x] != SHARED; + assume !EpochLeq(sx.R[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.R[x]))); + return; + SharedWriteRace: + ok := false; + assume sx.R[x] == SHARED; + assume !(forall j : int :: {f(j)} + 0 <= j && j < max(VCArrayLen(shadow.VC[st]),VCArrayLen(shadow.VC[sx])) && f(j) ==> + EpochLeq(VCArrayGet(shadow.VC[sx], j), VCArrayGet(shadow.VC[st], j))); + return; +} + +procedure {:yields} {:layer 20} {:refines "AtomicWrite"} Write({:linear "tid"} tid:Tid, x : Var) returns (ok : bool) + requires {:layer 10,20} ValidTid(tid); + requires {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 10,20} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); +{ + var e, w, vw, r, vr: Epoch; + + call Yield20(tid); + + call e := ThreadStateGetE(tid); + + // optional block + call w := VarStateGetWNoLock(tid, x); + if (w == e) { + // write same epoch + ok := true; + call Yield20(tid); + return; + } + + call Yield20(tid); + + call AcquireVarLock(tid, x); + call w := VarStateGetW(tid, x); + call vw := VCGetElem(tid, ShadowableTid(tid), tid#epoch(w)); + if (!EpochLeq(w, vw)) { + // write-write race + call ReleaseVarLock(tid, x); ok := false; call Yield20(tid); return; + } + call r := VarStateGetR(tid, x); + if (r != SHARED) { + call vr := VCGetElem(tid, ShadowableTid(tid), tid#epoch(r)); + if (!EpochLeq(r, vr)) { + // read-write race + call ReleaseVarLock(tid, x); ok := false; call Yield20(tid); return; + } + // write exclusive + call VarStateSetW(tid, x, e); + } else { + call ok := VC.Leq(tid, ShadowableVar(x), ShadowableTid(tid)); + if (!ok) { + // shared-write race + call ReleaseVarLock(tid, x); + ok := false; + call Yield20(tid); return; + } + // write shared + call VarStateSetW(tid, x, e); + } + + call ReleaseVarLock(tid, x); + ok := true; + call Yield20(tid); + return; +} + + +procedure {:atomic} {:layer 21,30} AtomicRead({:linear "tid"} tid:Tid, x : Var) returns (ok : bool) +modifies sx.R, shadow.VC; +{ + var st : Shadowable; + var sx : Shadowable; + + assert ValidTid(tid); + assert tid != nil && tid >= 0 && tid >= 0; + st := ShadowableTid(tid); + sx := ShadowableVar(x); + goto ReadSameEpoch, ReadSharedSameEpoch, ReadExclusive, ReadShared, ReadShare, WriteReadRace; + ReadSameEpoch: + ok := true; + assume sx.R[x] == VCArrayGet(shadow.VC[st], tid); + return; + ReadSharedSameEpoch: + ok := true; + assume sx.R[x] == SHARED; + assume VCArrayGet(shadow.VC[sx], tid) == VCArrayGet(shadow.VC[st], tid); + return; + ReadExclusive: + ok := true; + assume sx.R[x] != SHARED; + assume EpochLeq(sx.W[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.W[x]))); + assume EpochLeq(sx.R[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.R[x]))); + sx.R[x] := VCArrayGet(shadow.VC[st], tid); + return; + ReadShared: + ok := true; + assume sx.R[x] == SHARED; + assume EpochLeq(sx.W[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.W[x]))); + shadow.VC[sx][tid] := VCArrayGet(shadow.VC[st], tid); + shadow.VC[sx] := VCArraySetLen(shadow.VC[sx], max(VCArrayLen(shadow.VC[sx]), tid+1)); + return; + ReadShare: + assume sx.R[x] != SHARED; + assume EpochLeq(sx.W[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.W[x]))); + shadow.VC[sx] := VC.bottom(); + shadow.VC[sx][tid#epoch(sx.R[x])] := sx.R[x]; + shadow.VC[sx][tid] := VCArrayGet(shadow.VC[st], tid); + shadow.VC[sx] := VCArraySetLen(shadow.VC[sx], max(tid#epoch(sx.R[x])+1, tid+1)); + sx.R[x] := SHARED; + ok := true; + return; + WriteReadRace: + ok := false; + assume !EpochLeq(sx.W[x], VCArrayGet(shadow.VC[st], tid#epoch(sx.W[x]))); + return; +} + +procedure {:yields} {:layer 20} {:refines "AtomicRead"} Read({:linear "tid"} tid:Tid, x : Var) returns (ok : bool) + requires {:layer 10,20} ValidTid(tid); + requires {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 10,20} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); +{ + var e, w, vw, r, vr: Epoch; + var xVC, stVC: VC; + + call Yield20(tid); + + call e := ThreadStateGetE(tid); + + // optional block + if (*) { + // read same epoch + call r := VarStateGetRNoLock(tid, x); + assume r != SHARED; + if (r == e) { + ok := true; + call Yield20(tid); + return; + } + } else { + call r := VarStateGetRShared(tid, x); + assume r == SHARED; + call vw := VCGetElemShared(tid, x); + if (vw == e) { + ok := true; + call Yield20(tid); + return; + } + } + + call Yield20(tid); + + call AcquireVarLock(tid, x); + call w := VarStateGetW(tid, x); + call vw := VCGetElem(tid, ShadowableTid(tid), tid#epoch(w)); + if (!EpochLeq(w, vw)) { + // write-read race + call ReleaseVarLock(tid, x); + ok := false; + call Yield20(tid); + return; + } + call r := VarStateGetR(tid, x); + if (r != SHARED) { + call vr := VCGetElem(tid, ShadowableTid(tid), tid#epoch(r)); + if (EpochLeq(r, vr)) { + // Read Exclusive + assert {:layer 10,20} tid#epoch(e) >= 0; + call VarStateSetR(tid, x, e); + call ReleaseVarLock(tid, x); + ok := true; + call Yield20(tid); + return; + } else { + call VCInit(tid, ShadowableVar(x)); + call VCSetElem(tid, ShadowableVar(x), tid#epoch(r), r); + call VCSetElem(tid, ShadowableVar(x), tid, e); + call VarStateSetR(tid, x, SHARED); + call ReleaseVarLock(tid, x); + ok := true; + call Yield20(tid); + return; + } + } else { + // Read Shared + call VCSetElemShared(tid, x, e); + call ReleaseVarLock(tid, x); + ok := true; + call Yield20(tid); + return; + } +} + + +/****** Layer 30 --> 40 ******/ + + + +procedure {:yields} {:layer 30} Yield30({:linear "tid"} tid:Tid) + requires {:layer 10,20,30} ValidTid(tid); + requires {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + requires {:layer 30} thread.State[tid] == RUNNING(); + requires {:layer 30} (forall t: Tid :: thread.State[t] == UNUSED() ==> shadow.Lock[ShadowableTid(t)] == nil); + + ensures {:layer 10,20,30} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + ensures {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + ensures {:layer 30} thread.State[tid] == RUNNING(); + ensures {:layer 30} (forall t: Tid :: old(shadow.Lock)[ShadowableTid(t)] == tid ==> thread.State[t] == old(thread.State)[t]); + ensures {:layer 30} (forall t: Tid :: thread.State[t] == UNUSED() ==> shadow.Lock[ShadowableTid(t)] == nil); + +{ + yield; + + assert {:layer 10,20,30} ValidTid(tid); + + assert {:layer 10,20,30} LocksPreserved(tid, old(shadow.Lock), shadow.Lock); + assert {:layer 10,20} FTRepOk(shadow.VC, sx.W, sx.R); + + assert {:layer 10,20} FTPreserved(tid, old(shadow.Lock), old(shadow.VC), old(sx.W), old(sx.R), shadow.Lock, shadow.VC, sx.W, sx.R); + + assert {:layer 30} thread.State[tid] == RUNNING(); + assert {:layer 30} (forall t: Tid :: old(shadow.Lock)[ShadowableTid(t)] == tid ==> thread.State[t] == old(thread.State)[t]); + assert {:layer 30} (forall t: Tid :: thread.State[t] == UNUSED() ==> shadow.Lock[ShadowableTid(t)] == nil); +} + + +procedure {:yields} {:layer 30} Driver({:linear "tid"} tid:Tid) returns (ok: bool) + requires {:layer 10,20,30} ValidTid(tid); + requires {:layer 10,20,30} shadow.Lock[ShadowableTid(tid)] == tid; + requires {:layer 10,20} (forall s: Shadowable :: VCRepOk(shadow.VC[s])); + requires {:layer 10,20} VarsRepOk(sx.W, sx.R); + requires {:layer 30} thread.State[tid] == RUNNING(); + requires {:layer 30} (forall t: Tid :: thread.State[t] == UNUSED() ==> shadow.Lock[ShadowableTid(t)] == nil); +{ + var x: Var; + var l: Lock; + var uid: Tid; + call Yield30(tid); + ok := true; + while (ok) + invariant {:layer 10,20,30} ValidTid(tid); + invariant {:layer 10,20,30} shadow.Lock[ShadowableTid(tid)] == tid; + invariant {:layer 30} thread.State[tid] == RUNNING(); + invariant {:layer 10,20} (forall s: Shadowable :: VCRepOk(shadow.VC[s])); + invariant {:layer 10,20} VarsRepOk(sx.W, sx.R); + invariant {:layer 30} (forall t: Tid :: thread.State[t] == UNUSED() ==> shadow.Lock[ShadowableTid(t)] == nil); + { + if (*) { + havoc x; + call ok := Write(tid, x); + } else if (*) { + havoc x; + call ok := Read(tid, x); + } else if (*) { + assert {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + call l := ChooseLockToAcquire(tid); + call Yield30(tid); + call Acquire(tid, l); + } else if (*) { + call l := ChooseLockToRelease(tid); + call Yield30(tid); + call Release(tid, l); + call Yield30(tid); + call ReleaseChosenLock(tid, l); + call Yield30(tid); + } else if (*) { + call uid := AllocTid(tid); + assert {:layer 10,20} shadow.Lock[ShadowableTid(tid)] == tid; + assert {:layer 10,20} shadow.Lock[ShadowableTid(uid)] == tid; + assert {:layer 10,20} (forall s: Shadowable :: VCRepOk(shadow.VC[s])); + assert {:layer 10,20,30} tid != uid; + assert {:layer 10,20,30} ValidTid(tid); + assert {:layer 10,20,30} ValidTid(uid); + call Yield30(tid); + assert {:layer 10,20,30} ValidTid(tid); + assert {:layer 10,20,30} ValidTid(uid); + call Fork(tid, uid); + call Yield30(tid); + call StartThread(tid, uid); + call Yield30(tid); + } else { + call Yield30(tid); + call uid := ChooseThreadToJoin(tid); + call Yield30(tid); + call Join(tid, uid); + call Yield30(tid); + call ReleaseJoinLock(tid, uid); + call Yield30(tid); + } + call Yield30(tid); + assert {:layer 20} shadow.Lock[ShadowableTid(tid)] == tid; + } + yield; +} + +procedure {:yields} {:layer 0} {:refines "AtomicReleaseJoinLock"} ReleaseJoinLock({:linear "tid"} tid:Tid, uid: Tid); +procedure {:atomic} {:layer 1,30} AtomicReleaseJoinLock({:linear "tid"} tid:Tid, uid: Tid) +modifies shadow.Lock; +{ + assert ValidTid(tid); + assert ValidTid(uid); + assert tid != uid; + assert shadow.Lock[ShadowableTid(uid)] == tid; + shadow.Lock[ShadowableTid(uid)] := nil; +} + +procedure {:yields} {:layer 0} {:refines "AtomicChooseThreadToJoin"} ChooseThreadToJoin({:linear "tid"} tid:Tid) returns (uid: Tid); +procedure {:atomic} {:layer 1,30} AtomicChooseThreadToJoin({:linear "tid"} tid:Tid) returns (uid: Tid) +modifies shadow.Lock, thread.HasJoined; +{ + assert thread.State[tid] == RUNNING() && ValidTid(tid); + assume tid != uid; + assume thread.State[uid] == STOPPED() && ValidTid(uid); + assume shadow.Lock[ShadowableTid(uid)] == nil; + shadow.Lock[ShadowableTid(uid)] := tid; + thread.HasJoined[tid, uid] := true; +} + +procedure {:yields} {:layer 0} {:refines "AtomicAllocTid"} AllocTid({:linear "tid"} tid:Tid) returns (uid: Tid); +procedure {:atomic} {:layer 1,30} AtomicAllocTid({:linear "tid"} tid:Tid) returns (uid: Tid) +modifies thread.State, thread.ForkedBy, shadow.Lock; +{ + assert thread.State[tid] == RUNNING() && ValidTid(tid); + assert (forall t: Tid :: thread.State[t] == UNUSED() ==> shadow.Lock[ShadowableTid(t)] == nil); + assume tid != uid; + assume thread.State[uid] == UNUSED() && ValidTid(uid); + thread.State[uid] := NEW(); + thread.ForkedBy[uid] := tid; + shadow.Lock[ShadowableTid(uid)] := tid; + assume VCRepOk(shadow.VC[ShadowableTid(uid)]); +} + +procedure {:yields} {:layer 0} {:refines "AtomicStartThread"} StartThread({:linear "tid"} tid:Tid, uid: Tid); +procedure {:atomic} {:layer 1,30} AtomicStartThread({:linear "tid"} tid:Tid, uid: Tid) +modifies thread.State, shadow.Lock; +{ + assert ValidTid(tid); + assert ValidTid(uid); + assert tid != uid; + assert shadow.Lock[ShadowableTid(uid)] == tid; + assert thread.State[uid] == NEW(); + thread.State[uid] := RUNNING(); + shadow.Lock[ShadowableTid(uid)] := uid; +} + +procedure {:yields} {:layer 0} {:refines "AtomicChooseLockToAcquire"} ChooseLockToAcquire({:linear "tid"} tid:Tid) returns (l: Lock); +procedure {:atomic} {:layer 1,30} AtomicChooseLockToAcquire({:linear "tid"} tid:Tid) returns (l: Lock) +modifies shadow.Lock; +{ + assert ValidTid(tid); + assume shadow.Lock[ShadowableLock(l)] == nil; + shadow.Lock[ShadowableLock(l)] := tid; +} + +procedure {:yields} {:layer 0} {:refines "AtomicChooseLockToRelease"} ChooseLockToRelease({:linear "tid"} tid:Tid) returns (l: Lock); +procedure {:atomic} {:layer 1,30} AtomicChooseLockToRelease({:linear "tid"} tid:Tid) returns (l: Lock) +{ + assert ValidTid(tid); + assume shadow.Lock[ShadowableLock(l)] == tid; +} + +procedure {:yields} {:layer 0} {:refines "AtomicReleaseChosenLock"} ReleaseChosenLock({:linear "tid"} tid:Tid, l: Lock); +procedure {:atomic} {:layer 1,30} AtomicReleaseChosenLock({:linear "tid"} tid:Tid, l: Lock) +modifies shadow.Lock; +{ + assert ValidTid(tid); + assert shadow.Lock[ShadowableLock(l)] == tid; + shadow.Lock[ShadowableLock(l)] := nil; +} + + + + + + +// needed for linear Tids --- don't forget... +function {:builtin "MapConst"} TidMapConstBool(bool): [Tid]bool; + +function {:inline} {:linear "tid"} TidCollector(x: Tid) : [Tid]bool +{ + TidMapConstBool(false)[x := true] +} + +// for matching quantifiers +function {:inline false} f(i: int): bool { true } + + + + +function +{:witness "shadow.VC AtomicVC.Inc AtomicVC.Copy 11"} +{:witness "shadow.VC AtomicVC.Inc AtomicVC.Join 11"} +{:witness "shadow.VC AtomicVCSetElem AtomicVC.Copy 11"} +{:witness "shadow.VC AtomicVCSetElem AtomicVC.Join 11"} +{:witness "shadow.VC AtomicVCSetElemShared AtomicVC.Copy 11"} +{:witness "shadow.VC AtomicVCSetElemShared AtomicVC.Join 11"} +{:witness "shadow.VC AtomicVCInit AtomicVC.Copy 11"} +{:witness "shadow.VC AtomicVCInit AtomicVC.Join 11"} +{:witness "shadow.VC AtomicVC.Copy AtomicVC.Copy 11"} +{:witness "shadow.VC AtomicVC.Copy AtomicVC.Join 11"} +{:witness "shadow.VC AtomicVC.Join AtomicVC.Copy 11"} +{:witness "shadow.VC AtomicVC.Join AtomicVC.Join 11"} +{:witness "shadow.VC AtomicVC.Inc AtomicVC.Copy 20"} +{:witness "shadow.VC AtomicVC.Inc AtomicVC.Join 20"} +{:witness "shadow.VC AtomicVCSetElem AtomicVC.Copy 20"} +{:witness "shadow.VC AtomicVCSetElem AtomicVC.Join 20"} +{:witness "shadow.VC AtomicVCSetElemShared AtomicVC.Copy 20"} +{:witness "shadow.VC AtomicVCSetElemShared AtomicVC.Join 20"} +{:witness "shadow.VC AtomicVCInit AtomicVC.Copy 20"} +{:witness "shadow.VC AtomicVCInit AtomicVC.Join 20"} +{:witness "shadow.VC AtomicVC.Copy AtomicVC.Copy 20"} +{:witness "shadow.VC AtomicVC.Copy AtomicVC.Join 20"} +{:witness "shadow.VC AtomicVC.Join AtomicVC.Copy 20"} +{:witness "shadow.VC AtomicVC.Join AtomicVC.Join 20"} +witness (shadow.VC:[Shadowable]VC, shadow.VC':[Shadowable]VC, second_v1:Shadowable) : [Shadowable]VC +{ + shadow.VC[second_v1 := shadow.VC'[second_v1]] +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/verified-ft.bpl.expect boogie-2.4.1+dfsg/Test/civl/verified-ft.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/verified-ft.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/verified-ft.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1012 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/wilcox.bpl boogie-2.4.1+dfsg/Test/civl/wilcox.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/wilcox.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/wilcox.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,42 @@ +// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure {:layer 1} GhostRead() returns (oldx: int) +ensures x == oldx; +{ + oldx := x; +} + +var {:layer 0,2} x: int; + +procedure {:yields} {:layer 0} {:refines "AtomicIncX"} IncX(); + +procedure {:both} {:layer 1} AtomicIncX() +modifies x; +{ x := x + 1; } + +procedure {:yields} {:layer 1} {:refines "AtomicSlowAdd"} SlowAdd(n: int) +requires {:layer 1} n >= 0; +{ + var i: int; + var {:layer 1} oldx: int; + yield; + + call oldx := GhostRead(); + i := 0; + while (i < n) + invariant {:layer 1} i <= n; + invariant {:layer 1} x == oldx + i; + { + call IncX(); + i := i + 1; + } + + assert {:layer 1} i == n; + + yield; +} + +procedure {:both} {:layer 2} AtomicSlowAdd(n: int) +modifies x; +{ x := x + n; } diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/wilcox.bpl.expect boogie-2.4.1+dfsg/Test/civl/wilcox.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/wilcox.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/wilcox.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 4 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/write-barrier.bpl boogie-2.4.1+dfsg/Test/civl/write-barrier.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/write-barrier.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/write-barrier.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,113 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type Tid; +const unique nil: Tid; + +var {:layer 0,3} Color:int; +var {:layer 0,1} lock:Tid; + +function {:inline} UNALLOC():int { 0 } +function {:inline} WHITE():int { 1 } +function {:inline} GRAY():int { 2 } +function {:inline} BLACK():int { 3 } +function {:inline} Unalloc(i:int) returns(bool) { i <= 0 } +function {:inline} White(i:int) returns(bool) { i == 1 } +function {:inline} Gray(i:int) returns(bool) { i == 2 } +function {:inline} Black(i:int) returns(bool) { i >= 3 } +function {:inline} WhiteOrLighter(i:int) returns(bool) { i <= 1 } + +procedure {:yields} {:layer 2} YieldColorOnlyGetsDarker() +ensures {:layer 2} Color >= old(Color); +{ + yield; + assert {:layer 2} Color >= old(Color); +} + +procedure {:yields} {:layer 2} {:refines "AtomicWriteBarrier"} WriteBarrier({:linear "tid"} tid:Tid) +requires {:layer 2} Color >= WHITE(); +ensures {:layer 2} Color >= GRAY(); +{ + var colorLocal:int; + yield; + assert {:layer 2} Color >= WHITE(); + call colorLocal := GetColorNoLock(); + call YieldColorOnlyGetsDarker(); + if (WhiteOrLighter(colorLocal)) { call WriteBarrierSlow(tid); } + yield; + assert {:layer 2} Color >= GRAY(); +} + +procedure {:yields} {:layer 1} {:refines "AtomicWriteBarrier"} WriteBarrierSlow({:linear "tid"} tid:Tid) +{ + var colorLocal:int; + yield; + call AcquireLock(tid); + call colorLocal := GetColorLocked(tid); + if (WhiteOrLighter(colorLocal)) { call SetColorLocked(tid, GRAY()); } + call ReleaseLock(tid); + yield; +} + +procedure {:atomic} {:layer 2,3} AtomicWriteBarrier({:linear "tid"} tid:Tid) +modifies Color; +{ + assert tid != nil; + if (WhiteOrLighter(Color)) { + Color := GRAY(); + } +} + +procedure {:right} {:layer 1,1} AtomicAcquireLock({:linear "tid"} tid: Tid) +modifies lock; +{ + assert tid != nil; + assume lock == nil; + lock := tid; +} + +procedure {:left} {:layer 1,1} AtomicReleaseLock({:linear "tid"} tid: Tid) +modifies lock; +{ + assert tid != nil; + assert lock == tid; + lock := nil; +} + +procedure {:atomic} {:layer 1,1} AtomicSetColorLocked({:linear "tid"} tid:Tid, newCol:int) +modifies Color; +{ + assert tid != nil; + assert lock == tid; + Color := newCol; +} + +procedure {:both} {:layer 1,1} AtomicGetColorLocked({:linear "tid"} tid:Tid) returns (col:int) +{ + assert tid != nil; + assert lock == tid; + col := Color; +} + +procedure {:atomic} {:layer 1,2} AtomicGetColorNoLock() returns (col:int) +{ + col := Color; +} + +procedure {:yields} {:layer 0} {:refines "AtomicAcquireLock"} AcquireLock({:linear "tid"} tid: Tid); +procedure {:yields} {:layer 0} {:refines "AtomicReleaseLock"} ReleaseLock({:linear "tid"} tid: Tid); +procedure {:yields} {:layer 0} {:refines "AtomicSetColorLocked"} SetColorLocked({:linear "tid"} tid:Tid, newCol:int); +procedure {:yields} {:layer 0} {:refines "AtomicGetColorLocked"} GetColorLocked({:linear "tid"} tid:Tid) returns (col:int); +procedure {:yields} {:layer 0} {:refines "AtomicGetColorNoLock"} GetColorNoLock() returns (col:int); + +function {:builtin "MapConst"} MapConstBool(bool): [Tid]bool; +function {:builtin "MapOr"} MapOr([Tid]bool, [Tid]bool) : [Tid]bool; + +function {:inline} {:linear "tid"} TidCollector(x: Tid) : [Tid]bool +{ + MapConstBool(false)[x := true] +} +function {:inline} {:linear "tid"} TidSetCollector(x: [Tid]bool) : [Tid]bool +{ + x +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/write-barrier.bpl.expect boogie-2.4.1+dfsg/Test/civl/write-barrier.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/write-barrier.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/write-barrier.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 27 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/wsq.bpl boogie-2.4.1+dfsg/Test/civl/wsq.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/wsq.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/wsq.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -14,7 +14,7 @@ var {:layer 0,3} H: int; var {:layer 0,3} T: int; var {:layer 0,3} items: [int]int; -var {:layer 0} status: [int]bool; +var {:layer 0,4} status: [int]bool; var {:layer 0,3} take_in_cs: bool; var {:layer 0,3} put_in_cs: bool; var {:layer 0,3} steal_in_cs: [Tid]bool; @@ -42,11 +42,11 @@ h_ss:[Tid]int, t_ss:[Tid]int ):(bool) -{ +{ ( - ( (take_in_cs) && h_ss[ptTid] < t_ss[ptTid] ==> (t_ss[ptTid] == T && H <= T && + ( (take_in_cs) && h_ss[ptTid] < t_ss[ptTid] ==> (t_ss[ptTid] == T && H <= T && items[T] != EMPTY && status[T] == IN_Q) ) && - (put_in_cs ==> !take_in_cs) && (take_in_cs ==> !put_in_cs) && + (put_in_cs ==> !take_in_cs) && (take_in_cs ==> !put_in_cs) && (( (take_in_cs) && H != h_ss[ptTid]) ==> H > h_ss[ptTid]) && (forall td:Tid :: (stealerTid(td) && steal_in_cs[td] && H == h_ss[td] && H < t_ss[td]) ==> (items[H] != EMPTY && status[H] == IN_Q)) && (forall td:Tid :: (stealerTid(td) && steal_in_cs[td] && H != h_ss[td]) ==> H > h_ss[td]) @@ -57,488 +57,490 @@ put_in_cs:bool, take_in_cs:bool, items:[int]int, status: [int]bool, _H:int, _T:int):(bool) -{ +{ ( (forall i:int :: (_H <= i && i <= _T) ==> (status[i] == IN_Q && items[i] != EMPTY)) ) } function {:inline} emptyInv(put_in_cs:bool, take_in_cs:bool, items:[int]int, status:[int]bool, T:int):(bool) { - (forall i:int :: (i>=T && !put_in_cs && !take_in_cs) ==> status[i] == NOT_IN_Q && items[i] == EMPTY) + (forall i:int :: (i>=T && !put_in_cs && !take_in_cs) ==> status[i] == NOT_IN_Q && items[i] == EMPTY) } function {:inline} putInv(items:[int]int, status: [int]bool, H:int, T:int):(bool) -{ +{ (forall i:int :: (H <= i && i < T) ==> (status[i] == IN_Q && items[i] != EMPTY)) } function {:inline} takeInv(items:[int]int, status: [int]bool, H:int, T:int, t:int, h:int):(bool) -{ +{ (forall i:int :: (h <= i && i <= t) ==> (status[i] == IN_Q && items[i] != EMPTY) && - t == T + t == T ) } - -procedure {:yields} {:layer 3} put({:linear "tid"} tid:Tid, task: int) + +procedure {:atomic} {:layer 4} atomic_put({:linear "tid"} tid:Tid, task: int) +modifies status; +{ + var i: int; + assume status[i] == NOT_IN_Q; + status[i] := IN_Q; +} + +procedure {:yields} {:layer 3} {:refines "atomic_put"} put({:linear "tid"} tid:Tid, task: int) requires {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && task != EMPTY && !take_in_cs && !put_in_cs; requires {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); requires {:layer 3} {:expand} emptyInv(put_in_cs, take_in_cs, items,status,T); ensures {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; ensures {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); ensures {:layer 3} {:expand} emptyInv(put_in_cs, take_in_cs, items,status,T); -ensures {:atomic} |{ var i: int; A: assume status[i] == NOT_IN_Q; status[i] := IN_Q; return true; }|; { - var t: int; - var {:layer 3} oldH:int; - var {:layer 3} oldT:int; - var {:layer 3} oldStatusT:bool; - - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; - assert {:layer 3} {:expand} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H && oldT == T; - assert {:layer 3} {:expand} emptyInv(put_in_cs, take_in_cs, items,status,T); - - call t := readT_put(tid); - - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && put_in_cs; - assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} tid == ptTid && t == T; - assert {:layer 3} oldH <= H && oldT == T; - assert {:layer 3} (forall i:int :: i>=T ==> status[i] == NOT_IN_Q && items[i] == EMPTY); - - call writeItems_put(tid,t, task); - - call oldH, oldT := GhostRead(); - call oldStatusT := GhostReadStatus(); - yield; - assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && t == T && tid == ptTid && !take_in_cs && put_in_cs; - assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} items[t] == task; - assert {:layer 3} oldH <= H && oldT == T; - assert {:layer 3} (forall i:int :: i>T ==> status[i] == NOT_IN_Q && items[i] == EMPTY); - - - call writeT_put(tid, t+1); + var t: int; + var {:layer 3} oldH:int; + var {:layer 3} oldT:int; + var {:layer 3} oldStatusT:bool; + + yield; + assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; + assert {:layer 3} {:expand} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} {:expand} emptyInv(put_in_cs, take_in_cs, items,status,T); + + call t := readT_put(tid); + + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && put_in_cs; + assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} tid == ptTid && t == T; + assert {:layer 3} oldH <= H && oldT == T; + assert {:layer 3} (forall i:int :: i>=T ==> status[i] == NOT_IN_Q && items[i] == EMPTY); + + call writeItems_put(tid,t, task); + + call oldH, oldT := GhostRead(); + call oldStatusT := GhostReadStatus(); + yield; + assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && t == T && tid == ptTid && !take_in_cs && put_in_cs; + assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} items[t] == task; + assert {:layer 3} oldH <= H && oldT == T; + assert {:layer 3} (forall i:int :: i>T ==> status[i] == NOT_IN_Q && items[i] == EMPTY); + + + call writeT_put(tid, t+1); + + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; + assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} T == t + 1; + assert {:layer 3} oldH <= H && oldT == T; + assert {:layer 3} {:expand} emptyInv(put_in_cs, take_in_cs, items,status,T); +} - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; - assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} T == t + 1; - assert {:layer 3} oldH <= H && oldT == T; - assert {:layer 3} {:expand} emptyInv(put_in_cs, take_in_cs, items,status,T); +procedure {:atomic} {:layer 4} atomic_take({:linear "tid"} tid:Tid) returns (task: int, taskstatus: bool) +modifies status; +{ + var i: int; + if (*) { + assume status[i] == IN_Q; + status[i] := NOT_IN_Q; + } } -procedure {:yields} {:layer 3} take({:linear "tid"} tid:Tid) returns (task: int, taskstatus: bool) +procedure {:yields} {:layer 3} {:refines "atomic_take"} take({:linear "tid"} tid:Tid) returns (task: int, taskstatus: bool) requires {:layer 3} {:expand} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; requires {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); ensures {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs && (task != EMPTY ==> taskstatus == IN_Q); ensures {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); -ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; status[i] := NOT_IN_Q; return true; C: return true;}|; { - var h, t: int; - var chk: bool; - var {:layer 3} oldH:int; - var {:layer 3} oldT:int; - - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H && oldT == T; - - LOOP_ENTRY_1: - - while(true) - invariant {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; - invariant {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - { - - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; + var h, t: int; + var chk: bool; + var {:layer 3} oldH:int; + var {:layer 3} oldT:int; + + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + + while(true) + invariant {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; + invariant {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + { + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H && oldT == T; + assert {:layer 3} oldH <= H && oldT == T; - call t := readT_take_init(tid); + call t := readT_take_init(tid); - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} t == T; + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && !put_in_cs; + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} t == T; assert {:layer 3} items[t-1] == EMPTY ==> H > t-1; - assert {:layer 3} oldH <= H && oldT == T; - - t := t-1; - call writeT_take(tid, t); + assert {:layer 3} oldH <= H && oldT == T; + + t := t-1; + call writeT_take(tid, t); - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && !take_in_cs && !put_in_cs && t_ss[tid] == t; - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} t == T; - assert {:layer 3} items[t] == EMPTY ==> H > t; - assert {:layer 3} oldH <= H && oldT == T; - - call h := readH_take(tid); + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && !take_in_cs && !put_in_cs && t_ss[tid] == t; + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} t == T; + assert {:layer 3} items[t] == EMPTY ==> H > t; + assert {:layer 3} oldH <= H && oldT == T; + + call h := readH_take(tid); - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && take_in_cs && !put_in_cs && h_ss[tid] == h && t_ss[tid] == t; - assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} t == T; - assert {:layer 3} h <= H; - assert {:layer 3} items[t] == EMPTY ==> H > t; - assert {:layer 3} oldH <= H; + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && take_in_cs && !put_in_cs && h_ss[tid] == h && t_ss[tid] == t; + assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} t == T; + assert {:layer 3} h <= H; + assert {:layer 3} items[t] == EMPTY ==> H > t; + assert {:layer 3} oldH <= H; assert {:layer 3} oldT == T; assert {:layer 3} h <= H; assert {:layer 3} oldH == h; - if(t= h; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && take_in_cs && h_ss[tid] == h && t_ss[tid] == t; - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} t == T && task == items[T]; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && take_in_cs && h_ss[tid] == h && t_ss[tid] == t; + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} t == T && task == items[T]; assert {:layer 3} T > H ==> items[T] != EMPTY; - assert {:layer 3} oldH <= H && oldT == T && !put_in_cs && take_in_cs; + assert {:layer 3} oldH <= H && oldT == T && !put_in_cs && take_in_cs; - if(t>h) { - call takeExitCS(tid); + if(t>h) { + call takeExitCS(tid); - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && h_ss[tid] == h && t_ss[tid] == t; - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} t == T && task == items[t] && task != EMPTY && taskstatus == IN_Q; - assert {:layer 3} oldH <= H && oldT == T && !put_in_cs && !take_in_cs; - return; - } - call writeT_take_eq(tid, h+1); - call oldH, oldT := GhostRead(); - - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} T == h + 1; - assert {:layer 3} oldH <= H; - assert {:layer 3} oldT == T; - assert {:layer 3} task == items[t]; - assert {:layer 3} !put_in_cs; - - call chk := CAS_H_take(tid, h,h+1); - - - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} chk ==> (h+1 == oldH && h_ss[tid] == oldH -1 && task != EMPTY); - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && !take_in_cs && h_ss[tid] == h && t_ss[tid] == t; + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} t == T && task == items[t] && task != EMPTY && taskstatus == IN_Q; + assert {:layer 3} oldH <= H && oldT == T && !put_in_cs && !take_in_cs; + return; + } + call writeT_take_eq(tid, h+1); + call oldH, oldT := GhostRead(); + + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} h+1 == T; - assert {:layer 3} task == items[t]; - assert {:layer 3} !take_in_cs; - assert {:layer 3} !put_in_cs; - assert {:layer 3} oldH <= H && oldT == T; - - if(!chk) { + assert {:layer 3} T == h + 1; + assert {:layer 3} oldH <= H; + assert {:layer 3} oldT == T; + assert {:layer 3} task == items[t]; + assert {:layer 3} !put_in_cs; - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} h+1 == T && task == items[t] && !take_in_cs && !put_in_cs; - assert {:layer 3} oldH <= H && oldT == T; - - goto LOOP_ENTRY_1; - } + call chk := CAS_H_take(tid, h,h+1); - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} chk ==> (h+1 == oldH && h_ss[tid] == oldH -1 && task != EMPTY); + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} h+1 == T && task == items[t] && !take_in_cs && !put_in_cs; - assert {:layer 3} oldH <= H && oldT == T && task != EMPTY && taskstatus == IN_Q; - - return; - } - - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && !put_in_cs; - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H && oldT == T; + assert {:layer 3} h+1 == T; + assert {:layer 3} task == items[t]; + assert {:layer 3} !take_in_cs; + assert {:layer 3} !put_in_cs; + assert {:layer 3} oldH <= H && oldT == T; + + if (chk) { + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} h+1 == T && task == items[t] && !take_in_cs && !put_in_cs; + assert {:layer 3} oldH <= H && oldT == T && task != EMPTY && taskstatus == IN_Q; + return; + } + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && tid == ptTid && h_ss[tid] == h && t_ss[tid] == t; + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} h+1 == T && task == items[t] && !take_in_cs && !put_in_cs; + assert {:layer 3} oldH <= H && oldT == T; + } + + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T) && tid == ptTid && !put_in_cs; + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} oldH <= H && oldT == T; } +procedure {:atomic} {:layer 4} atomic_steal({:linear "tid"} tid:Tid) returns (task: int, taskstatus: bool) +modifies status; +{ + var i: int; + if (*) { + assume status[i] == IN_Q; + status[i] := NOT_IN_Q; + } +} -procedure {:yields}{:layer 3} steal({:linear "tid"} tid:Tid) returns (task: int, taskstatus: bool) +procedure {:yields} {:layer 3} {:refines "atomic_steal"} steal({:linear "tid"} tid:Tid) returns (task: int, taskstatus: bool) requires {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && stealerTid(tid) && !steal_in_cs[tid]; requires {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); ensures {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1) && !steal_in_cs[tid] && (task != EMPTY ==> taskstatus == IN_Q); ensures {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); -ensures {:atomic} |{ var i: int; A: goto B,C; B: assume status[i] == IN_Q; status[i] := NOT_IN_Q; return true; C: return true;}|; { - var h, t: int; - var chk: bool; - var {:layer 3} oldH:int; - var {:layer 3} oldT:int; - - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} stealerTid(tid); - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H; - assert {:layer 3} !steal_in_cs[tid]; - - LOOP_ENTRY_2: - while(true) - invariant {:layer 3} stealerTid(tid); - invariant {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); - invariant {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - invariant {:layer 3} !steal_in_cs[tid]; - { - call oldH, oldT := GhostRead(); - yield; + var h, t: int; + var chk: bool; + var {:layer 3} oldH:int; + var {:layer 3} oldT:int; + + yield; + assert {:layer 3} stealerTid(tid); + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} !steal_in_cs[tid]; + + while(true) + invariant {:layer 3} stealerTid(tid); + invariant {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); + invariant {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + invariant {:layer 3} !steal_in_cs[tid]; + { + call oldH, oldT := GhostRead(); + yield; assert {:layer 3} stealerTid(tid); - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H; - assert {:layer 3} !steal_in_cs[tid]; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} oldH <= H; + assert {:layer 3} !steal_in_cs[tid]; - call h := readH_steal(tid); + call h := readH_steal(tid); - call oldH, oldT := GhostRead(); - yield; + call oldH, oldT := GhostRead(); + yield; assert {:layer 3} H >= h; assert {:layer 3} !steal_in_cs[tid]; - assert {:layer 3} h_ss[tid] == h; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); - assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H; + assert {:layer 3} h_ss[tid] == h; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); + assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} oldH <= H; - call t := readT_steal(tid); + call t := readT_steal(tid); - call oldH, oldT := GhostRead(); - yield; + call oldH, oldT := GhostRead(); + yield; assert {:layer 3} steal_in_cs[tid]; assert {:layer 3} stealerTid(tid) && H >= h && steal_in_cs[tid] && h_ss[tid] == h; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H && t == t_ss[tid]; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} oldH <= H && t == t_ss[tid]; assert {:layer 3} (h < t && take_in_cs && (h_ss[ptTid] < t_ss[ptTid]) && h == H) ==> (H < T); assert {:layer 3} H >= h; - if( h>= t) { - - task := EMPTY; - call stealExitCS(tid); - - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} !steal_in_cs[tid]; - assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid] && h_ss[tid] == h; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H; - return; - } + if( h>= t) { + task := EMPTY; + call stealExitCS(tid); + + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} !steal_in_cs[tid]; + assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid] && h_ss[tid] == h; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} oldH <= H; + return; + } - call task, taskstatus := readItems(tid, h); + call task, taskstatus := readItems(tid, h); call oldH, oldT := GhostRead(); - yield; + yield; assert {:layer 3} stealerTid(tid) && steal_in_cs[tid] && h_ss[tid] == h; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} oldH <= H; assert {:layer 3} oldH == H && H == h && h_ss[tid] == h ==> task != EMPTY; assert {:layer 3} (take_in_cs && (h_ss[ptTid] < t_ss[ptTid]) && h == H) ==> (H < T); - assert {:layer 3} h == H ==> status[H] == IN_Q; + assert {:layer 3} h == H ==> status[H] == IN_Q; - call chk := CAS_H_steal(tid, h,h+1); + call chk := CAS_H_steal(tid, h,h+1); - call oldH, oldT := GhostRead(); - yield; + call oldH, oldT := GhostRead(); + yield; assert {:layer 3} h_ss[tid] == h; assert {:layer 3} chk ==> (h+1 == oldH && h_ss[tid] == h && task != EMPTY && taskstatus == IN_Q); - assert {:layer 3} (take_in_cs && (h_ss[ptTid] < t_ss[ptTid]) && chk) ==> ((oldH-1) < T); + assert {:layer 3} (take_in_cs && (h_ss[ptTid] < t_ss[ptTid]) && chk) ==> ((oldH-1) < T); assert {:layer 3} {:expand} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid]; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); - assert {:layer 3} oldH <= H; - - if(!chk) { - goto LOOP_ENTRY_2; - } + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); + assert {:layer 3} oldH <= H; - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid]; - assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); - assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); - assert {:layer 3} oldH <= H && task != EMPTY; - return; - } + if(chk) { + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid]; + assert {:layer 3} queueInv(steal_in_cs,put_in_cs,take_in_cs,items, status, H, T-1); + assert {:layer 3} ideasInv(put_in_cs,items, status, H, T, take_in_cs, steal_in_cs, h_ss, t_ss); + assert {:layer 3} oldH <= H && task != EMPTY; + return; + } + } - call oldH, oldT := GhostRead(); - yield; - assert {:layer 3} chk && task != EMPTY; - assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid]; - assert {:layer 3} oldH <= H; + call oldH, oldT := GhostRead(); + yield; + assert {:layer 3} chk && task != EMPTY; + assert {:layer 3} stealerTid(tid) && !steal_in_cs[tid]; + assert {:layer 3} oldH <= H; } procedure {:layer 3} {:inline 1} GhostRead() returns (oldH: int, oldT: int) { - oldH := H; - oldT := T; + oldH := H; + oldT := T; } procedure {:layer 3} {:inline 1} GhostReadStatus() returns (oldStatus: bool) { - oldStatus := status[T]; + oldStatus := status[T]; +} + +procedure {:atomic} {:layer 1,3} atomic_readH_take({:linear "tid"} tid:Tid) returns (y: int) +modifies take_in_cs, h_ss; +{ assert tid == ptTid; y := H; take_in_cs := true; h_ss[tid] := H; } + +procedure {:yields} {:layer 0} {:refines "atomic_readH_take"} readH_take({:linear "tid"} tid:Tid) returns (y: int); + +procedure {:atomic} {:layer 1,3} atomic_readH_steal({:linear "tid"} tid:Tid) returns (y: int) +modifies h_ss; +{ assert stealerTid(tid); assert !steal_in_cs[tid]; y := H; h_ss[tid] := H; } + +procedure {:yields} {:layer 0} {:refines "atomic_readH_steal"} readH_steal({:linear "tid"} tid:Tid) returns (y: int); + +procedure {:atomic} {:layer 1,3} atomic_readT_take_init({:linear "tid"} tid:Tid) returns (y: int) +{ assert tid != NIL; assert tid == ptTid; y := T; } + +procedure {:yields} {:layer 0} {:refines "atomic_readT_take_init"} readT_take_init({:linear "tid"} tid:Tid) returns (y: int); + +procedure {:atomic} {:layer 1,3} atomic_readT_put({:linear "tid"} tid:Tid) returns (y: int) +modifies put_in_cs; +{ assert tid != NIL; assert tid == ptTid; put_in_cs := true; y := T; } + +procedure {:yields} {:layer 0} {:refines "atomic_readT_put"} readT_put({:linear "tid"} tid:Tid) returns (y: int); + +procedure {:atomic} {:layer 1,3} atomic_readT_steal({:linear "tid"} tid:Tid) returns (y: int) +modifies t_ss, steal_in_cs; +{ assert tid != NIL; assert stealerTid(tid); assert !steal_in_cs[tid]; y := T; t_ss[tid] := T; steal_in_cs[tid] := true; } + +procedure {:yields} {:layer 0} {:refines "atomic_readT_steal"} readT_steal({:linear "tid"} tid:Tid) returns (y: int); + +procedure {:atomic} {:layer 1,3} atomic_readItems({:linear "tid"} tid:Tid, ind: int) returns (y: int, b:bool) +{ y := items[ind]; b := status[ind]; } + +procedure {:yields} {:layer 0} {:refines "atomic_readItems"} readItems({:linear "tid"} tid:Tid, ind: int) returns (y: int, b:bool); + +procedure {:atomic} {:layer 1,3} atomic_writeT_put({:linear "tid"} tid:Tid, val: int) +modifies T, put_in_cs; +{ assert tid == ptTid; T := T+1; put_in_cs := false; } + +procedure {:yields} {:layer 0} {:refines "atomic_writeT_put"} writeT_put({:linear "tid"} tid:Tid, val: int); + +procedure {:atomic} {:layer 1,3} atomic_writeT_take({:linear "tid"} tid:Tid, val: int) +modifies T, t_ss; +{ assert tid == ptTid; T := val; t_ss[tid] := val; } + +procedure {:yields} {:layer 0} {:refines "atomic_writeT_take"} writeT_take({:linear "tid"} tid:Tid, val: int); + +procedure {:atomic} {:layer 1,3} atomic_writeT_take_abort({:linear "tid"} tid:Tid, val: int) +modifies T, take_in_cs; +{ assert tid == ptTid; assert take_in_cs; T := val; take_in_cs := false; } + +procedure {:yields} {:layer 0} {:refines "atomic_writeT_take_abort"} writeT_take_abort({:linear "tid"} tid:Tid, val: int); + +procedure {:atomic} {:layer 1,3} atomic_writeT_take_eq({:linear "tid"} tid:Tid, val: int) +modifies T; +{ assert tid == ptTid; T := val; } + +procedure {:yields} {:layer 0} {:refines "atomic_writeT_take_eq"} writeT_take_eq({:linear "tid"} tid:Tid, val: int); + +procedure {:atomic} {:layer 1,3} atomic_takeExitCS({:linear "tid"} tid:Tid) +modifies take_in_cs; +{ assert tid == ptTid; take_in_cs := false; } + +procedure {:yields} {:layer 0} {:refines "atomic_takeExitCS"} takeExitCS({:linear "tid"} tid:Tid); + +procedure {:atomic} {:layer 1,3} atomic_stealExitCS({:linear "tid"} tid:Tid) +modifies steal_in_cs; +{ assert stealerTid(tid); assert steal_in_cs[tid]; steal_in_cs[tid] := false; } + +procedure {:yields} {:layer 0} {:refines "atomic_stealExitCS"} stealExitCS({:linear "tid"} tid:Tid); + +procedure {:atomic} {:layer 1,3} atomic_writeItems({:linear "tid"} tid:Tid, idx: int, val: int) +modifies items, status; +{ assert tid == ptTid; assert val != EMPTY; items[idx] := val; status[idx] := IN_Q; } + +procedure {:yields} {:layer 0} {:refines "atomic_writeItems"} writeItems({:linear "tid"} tid:Tid, idx: int, val: int); + +procedure {:atomic} {:layer 1,3} atomic_writeItems_put({:linear "tid"} tid:Tid, idx: int, val: int) +modifies items, status; +{ assert tid == ptTid; assert val != EMPTY; items[idx] := val; status[idx] := IN_Q; } + +procedure {:yields} {:layer 0} {:refines "atomic_writeItems_put"} writeItems_put({:linear "tid"} tid:Tid, idx: int, val: int); + +procedure {:atomic} {:layer 1,3} atomic_CAS_H_take({:linear "tid"} tid:Tid, prevVal :int, val: int) returns (result: bool) +modifies status, H, take_in_cs; +{ + assert tid == ptTid; + if (H == prevVal) { + status[H] := NOT_IN_Q; + H := H+1; + result := true; + take_in_cs := false; + } else { + result := false; + take_in_cs := false; + } +} + +procedure {:yields} {:layer 0} {:refines "atomic_CAS_H_take"} CAS_H_take({:linear "tid"} tid:Tid, prevVal :int, val: int) returns (result: bool); + +procedure {:atomic} {:layer 1,3} atomic_CAS_H_steal({:linear "tid"} tid:Tid, prevVal :int, val: int) returns (result: bool) +modifies status, H, steal_in_cs; +{ + assert stealerTid(tid); + if (H == prevVal) { + status[H] := NOT_IN_Q; + H := H+1; + result := true; + steal_in_cs[tid] := false; + } else { + result := false; + steal_in_cs[tid] := false; + } } -procedure {:yields}{:layer 0,3} readH_take({:linear "tid"} tid:Tid) returns (y: int); -ensures {:atomic} |{A: assert tid == ptTid; - y := H; - take_in_cs := true; - h_ss[tid] := H; - return true;}|; - -procedure {:yields}{:layer 0,3} readH_steal({:linear "tid"} tid:Tid) returns (y: int); -ensures {:atomic} |{A: assert stealerTid(tid); - assert !steal_in_cs[tid]; - y := H; - h_ss[tid] := H; - return true;}|; - -procedure {:yields}{:layer 0,3} readT_take_init({:linear "tid"} tid:Tid) returns (y: int); -ensures {:atomic} |{A: assert tid != NIL; assert tid == ptTid; y := T; return true;}|; - -procedure {:yields}{:layer 0,3} readT_put({:linear "tid"} tid:Tid) returns (y: int); -ensures {:atomic} |{A: assert tid != NIL; - assert tid == ptTid; - put_in_cs := true; - y := T; - return true;}|; - -procedure {:yields}{:layer 0,3} readT_steal({:linear "tid"} tid:Tid) returns (y: int); -ensures {:atomic} |{A: assert tid != NIL; - assert stealerTid(tid); - assert !steal_in_cs[tid]; - y := T; - t_ss[tid] := T; - steal_in_cs[tid] := true; - return true;}|; - -procedure {:yields}{:layer 0,3} readItems({:linear "tid"} tid:Tid, ind: int) returns (y: int, b:bool); -ensures {:atomic} |{A: y := items[ind]; b := status[ind]; return true; }|; - -procedure {:yields}{:layer 0,3} writeT_put({:linear "tid"} tid:Tid, val: int); -ensures {:atomic} |{A: assert tid == ptTid; - T := T+1; - put_in_cs := false; - return true; }|; - -procedure {:yields}{:layer 0,3} writeT_take({:linear "tid"} tid:Tid, val: int); -ensures {:atomic} |{A: assert tid == ptTid; - T := val; - t_ss[tid] := val; - return true; }|; - -procedure {:yields}{:layer 0,3} writeT_take_abort({:linear "tid"} tid:Tid, val: int); -ensures {:atomic} |{A: assert tid == ptTid; - assert take_in_cs; - T := val; - take_in_cs := false; - return true; }|; - -procedure {:yields}{:layer 0,3} writeT_take_eq({:linear "tid"} tid:Tid, val: int); -ensures {:atomic} |{A: assert tid == ptTid; - T := val; - return true; }|; - -procedure {:yields}{:layer 0,3} takeExitCS({:linear "tid"} tid:Tid); -ensures {:atomic} |{A: assert tid == ptTid; - take_in_cs := false; - return true; }|; - -procedure {:yields}{:layer 0,3} stealExitCS({:linear "tid"} tid:Tid); -ensures {:atomic} |{A: assert stealerTid(tid); - assert steal_in_cs[tid]; - steal_in_cs[tid] := false; - return true; }|; - - -procedure {:yields}{:layer 0,3} writeItems({:linear "tid"} tid:Tid, idx: int, val: int); -ensures {:atomic} |{A: assert tid == ptTid; - assert val != EMPTY; - items[idx] := val; - status[idx] := IN_Q; - return true; }|; - - -procedure {:yields}{:layer 0,3} writeItems_put({:linear "tid"} tid:Tid, idx: int, val: int); -ensures {:atomic} |{A: assert tid == ptTid; - assert val != EMPTY; - items[idx] := val; - status[idx] := IN_Q; - return true; }|; - -procedure {:yields}{:layer 0,3} CAS_H_take({:linear "tid"} tid:Tid, prevVal :int, val: int) - returns (result: bool); -ensures {:atomic} |{ A: assert tid == ptTid; - goto B, C; - B: assume H == prevVal; - take_in_cs := false; - status[H] := NOT_IN_Q; - H := H+1; - result := true; - return true; - C: assume H != prevVal; result := false; - take_in_cs := false; - return true; -}|; - -procedure {:yields}{:layer 0,3} CAS_H_steal({:linear "tid"} tid:Tid, prevVal :int, val: int) - returns (result: bool); -ensures {:atomic} |{ A: assert stealerTid(tid); - goto B, C; - B: assume H == prevVal; - status[H] := NOT_IN_Q; - H := H+1; - result := true; - steal_in_cs[tid] := false; - return true; - C: assume H != prevVal; - result := false; - steal_in_cs[tid] := false; - return true; - }|; \ No newline at end of file +procedure {:yields} {:layer 0} {:refines "atomic_CAS_H_steal"} CAS_H_steal({:linear "tid"} tid:Tid, prevVal :int, val: int) returns (result: bool); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/wsq.bpl.expect boogie-2.4.1+dfsg/Test/civl/wsq.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/wsq.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/wsq.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,2 +1,2 @@ -Boogie program verifier finished with 6 verified, 0 errors +Boogie program verifier finished with 41 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/zeldovich.bpl boogie-2.4.1+dfsg/Test/civl/zeldovich.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/zeldovich.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/zeldovich.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,109 @@ +// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type Tid; +const nil:Tid; + +var {:layer 0,1} lock_x:Tid; +var {:layer 0,1} lock_y:Tid; +var {:layer 0,2} x:int; +var {:layer 0,2} y:int; + +procedure {:atomic}{:layer 2} GET_X ({:linear "tid"} tid:Tid) returns (v:int) +{ + v := x; +} + +procedure {:atomic}{:layer 2} SET_BOTH ({:linear "tid"} tid:Tid, v:int, w:int) +modifies x, y; +{ + x := v; + y := w; +} + +procedure {:layer 1}{:yields}{:refines "GET_X"} get_x ({:linear "tid"} tid:Tid) returns (v:int) +requires {:layer 1} tid != nil; +{ + yield; assert {:layer 1} tid != nil; + call acquire_x(tid); + call v := read_x(tid); + call release_x(tid); + yield; +} + +procedure {:layer 1}{:yields}{:refines "SET_BOTH"} set_both ({:linear "tid"} tid:Tid, v:int, w:int) +requires {:layer 1} tid != nil; +{ + yield; assert {:layer 1} tid != nil; + call acquire_x(tid); + call acquire_y(tid); + call write_x(tid, v); + call release_x(tid); // early release of lock_x + call write_y(tid, w); + call release_y(tid); + yield; +} + +procedure {:right}{:layer 1} ACQUIRE_X ({:linear "tid"} tid:Tid) +modifies lock_x; +{ + assert tid != nil; + assume lock_x == nil; + lock_x := tid; +} + +procedure {:left}{:layer 1} RELEASE_X ({:linear "tid"} tid:Tid) +modifies lock_x; +{ + assert tid != nil && lock_x == tid; + lock_x := nil; +} + +procedure {:right}{:layer 1} ACQUIRE_Y ({:linear "tid"} tid:Tid) +modifies lock_y; +{ + assert tid != nil; + assume lock_y == nil; + lock_y := tid; +} + +procedure {:left}{:layer 1} RELEASE_Y ({:linear "tid"} tid:Tid) +modifies lock_y; +{ + assert tid != nil && lock_y == tid; + lock_y := nil; +} + +procedure {:both}{:layer 1} WRITE_X ({:linear "tid"} tid:Tid, v:int) +modifies x; +{ + assert tid != nil && lock_x == tid; + x := v; +} + +procedure {:both}{:layer 1} WRITE_Y ({:linear "tid"} tid:Tid, v:int) +modifies y; +{ + assert tid != nil && lock_y == tid; + y := v; +} + +procedure {:both}{:layer 1} READ_X ({:linear "tid"} tid:Tid) returns (r:int) +{ + assert tid != nil && lock_x == tid; + r := x; +} + +procedure {:yields}{:layer 0}{:refines "ACQUIRE_X"} acquire_x ({:linear "tid"} tid:Tid); +procedure {:yields}{:layer 0}{:refines "ACQUIRE_Y"} acquire_y ({:linear "tid"} tid:Tid); +procedure {:yields}{:layer 0}{:refines "RELEASE_X"} release_x ({:linear "tid"} tid:Tid); +procedure {:yields}{:layer 0}{:refines "RELEASE_Y"} release_y ({:linear "tid"} tid:Tid); +procedure {:yields}{:layer 0}{:refines "WRITE_X"} write_x ({:linear "tid"} tid:Tid, v:int); +procedure {:yields}{:layer 0}{:refines "WRITE_Y"} write_y ({:linear "tid"} tid:Tid, v:int); +procedure {:yields}{:layer 0}{:refines "READ_X"} read_x ({:linear "tid"} tid:Tid) returns (r:int); + +function {:builtin "MapConst"} MapConstBool(bool) : [Tid]bool; +function {:inline}{:linear "tid"} TidCollector(tid:Tid) : [Tid]bool +{ + MapConstBool(false)[tid := true] +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/zeldovich.bpl.expect boogie-2.4.1+dfsg/Test/civl/zeldovich.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/civl/zeldovich.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/civl/zeldovich.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 38 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/datatypes/t3.bpl boogie-2.4.1+dfsg/Test/datatypes/t3.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/datatypes/t3.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/datatypes/t3.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,7 @@ +// RUN: %boogie -typeEncoding:m "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +type {:datatype} Value; +function {:constructor} Int(i: int): Value; + +type {:datatype} Path; +function {:constructor} Int(i: int): Path; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/datatypes/t3.bpl.expect boogie-2.4.1+dfsg/Test/datatypes/t3.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/datatypes/t3.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/datatypes/t3.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ +t3.bpl(7,25): error: more than one declaration of datatype constructor name: Int +1 parse errors detected in t3.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators1.bpl boogie-2.4.1+dfsg/Test/floats/BasicOperators1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators1.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperators1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,29 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure main() returns () { + var x : float24e8; + var y : float24e8; + var z : float24e8; + + z := x + y; + z := x - y; + z := x * y; + assume(!FEQ(y, 0x0.0e0f24e8)); + z := x / y; + + z := (0x1.0e0f24e8 + 0x1.0e0f24e8 + 0x0.0e0f24e8); + assert(z == 0x2.0e0f24e8); + + z := 0x2.0e0f24e8 - 0x1.0e0f24e8; + assert(z == 0x1.0e0f24e8); + + z := 0x1.0e0f24e8 * 0x1.0e0f24e8; + assert(z == 0x1.0e0f24e8); + + z := 0x1.0e0f24e8 / 0x1.0e0f24e8; + assert(z == 0x1.0e0f24e8); + + return; +} + +function {:builtin "fp.eq"} FEQ(float24e8,float24e8) returns (bool); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators1.bpl.expect boogie-2.4.1+dfsg/Test/floats/BasicOperators1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperators1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators2.bpl boogie-2.4.1+dfsg/Test/floats/BasicOperators2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators2.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperators2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,14 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure main() returns () { + var x : float53e11; + var y : float53e11; + var z : float53e11; + var r : float53e11; + x := 0x1.0e10f53e11; + y := x + 0x1.0e0f53e11; + z := x - 0x1.0e0f53e11; + r := y - z; + + assert r == 0x2.0e0f53e11; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators2.bpl.expect boogie-2.4.1+dfsg/Test/floats/BasicOperators2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperators2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators3.bpl boogie-2.4.1+dfsg/Test/floats/BasicOperators3.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators3.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperators3.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,13 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure main() returns () { + var x : float24e8; + var y : float24e8; + var z : float24e8; + var r : float24e8; + x := 0x1.0e10f24e8; + y := x + 0x1.0e0f24e8; + z := x - 0x1.0e0f24e8; + r := y - z; + assert r == 0x2.0e0f24e8; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators3.bpl.expect boogie-2.4.1+dfsg/Test/floats/BasicOperators3.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators3.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperators3.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,5 @@ +BasicOperators3.bpl(12,3): Error BP5001: This assertion might not hold. +Execution trace: + BasicOperators3.bpl(8,5): anon0 + +Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators4.bpl boogie-2.4.1+dfsg/Test/floats/BasicOperators4.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators4.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperators4.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,18 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type float = float24e8; +type f = float; + +procedure foo(x : float) returns (r : f) +{ + r := 0x2.0e0f24e8; + r := 0x1.000002e0f24e8; + r := x; + r := x * 0x1.000002e0f24e8; + r := x + 0x1.000002e0f24e8; + r := 0x1.0e0f24e8 + 0x1.0e0f24e8; + assert(r == 0x2.0e0f24e8); + + return; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators4.bpl.expect boogie-2.4.1+dfsg/Test/floats/BasicOperators4.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperators4.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperators4.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperatorsWithTypeConv.bpl boogie-2.4.1+dfsg/Test/floats/BasicOperatorsWithTypeConv.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperatorsWithTypeConv.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperatorsWithTypeConv.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,17 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_INT(int) returns (float24e8); +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_REAL(real) returns (float24e8); + +procedure main() returns () { + var x : float24e8; + var y : float24e8; + var z : float24e8; + var r : float24e8; + x := TO_FLOAT32_REAL(1e7); + y := x + TO_FLOAT32_INT(1); + z := x - TO_FLOAT32_INT(1); + r := y - z; + assert r == TO_FLOAT32_INT(2); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperatorsWithTypeConv.bpl.expect boogie-2.4.1+dfsg/Test/floats/BasicOperatorsWithTypeConv.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/BasicOperatorsWithTypeConv.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/BasicOperatorsWithTypeConv.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/CastToLowerPrec.bpl boogie-2.4.1+dfsg/Test/floats/CastToLowerPrec.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/CastToLowerPrec.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/CastToLowerPrec.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,16 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_FLOAT32(float24e8) returns (float53e11); +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_FLOAT64(float53e11) returns (float24e8); +function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_INT(int) returns (float53e11); +function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_REAL(real) returns (float53e11); + +procedure main() returns () { + var x : float53e11; + var y : float24e8; + + x := TO_FLOAT64_REAL(1e20)+TO_FLOAT64_INT(1); + y := TO_FLOAT32_FLOAT64(x); + assert x != TO_FLOAT64_FLOAT32(y); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/CastToLowerPrec.bpl.expect boogie-2.4.1+dfsg/Test/floats/CastToLowerPrec.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/CastToLowerPrec.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/CastToLowerPrec.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax1.bpl boogie-2.4.1+dfsg/Test/floats/ConstSyntax1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax1.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/ConstSyntax1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,14 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure foo(x : real) returns (r : float8e24) +{ + r := 15; // Error + r := 15.0; // Error + r := 0x4.0e-32f22e8; // Error + r := 0x0.00001e-32f23e8; // Error + r := x; // Error + r := 0x0.00001e-32f23e8 + 0x0.00001e-32f23e8; // Error + r := 0x0.000008e-32f24e8 + 0x0.00001e-32f23e8; // Error + + return; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax1.bpl.expect boogie-2.4.1+dfsg/Test/floats/ConstSyntax1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/ConstSyntax1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,8 @@ +ConstSyntax1.bpl(5,2): Error: mismatched types in assignment command (cannot assign int to float8e24) +ConstSyntax1.bpl(6,2): Error: mismatched types in assignment command (cannot assign real to float8e24) +ConstSyntax1.bpl(7,2): Error: mismatched types in assignment command (cannot assign float22e8 to float8e24) +ConstSyntax1.bpl(8,2): Error: mismatched types in assignment command (cannot assign float23e8 to float8e24) +ConstSyntax1.bpl(9,2): Error: mismatched types in assignment command (cannot assign real to float8e24) +ConstSyntax1.bpl(10,2): Error: mismatched types in assignment command (cannot assign float23e8 to float8e24) +ConstSyntax1.bpl(11,27): Error: invalid argument types (float24e8 and float23e8) to binary operator + +7 type checking errors detected in ConstSyntax1.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax2.bpl boogie-2.4.1+dfsg/Test/floats/ConstSyntax2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax2.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/ConstSyntax2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,13 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure foo(x : float24e8) returns (r : float24e8) +{ + r := 0x2.0e0f24e8; + r := 0x1.000002e0f24e8; + r := x; + r := x + 0x1.000002e0f24e8; + r := 0x1.0e0f24e8 + 0x1.0e0f24e8; + assert(r == 0x2.0e0f24e8); + + return; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax2.bpl.expect boogie-2.4.1+dfsg/Test/floats/ConstSyntax2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/ConstSyntax2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax3.bpl boogie-2.4.1+dfsg/Test/floats/ConstSyntax3.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax3.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/ConstSyntax3.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,33 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure main() returns () { + + var a : float2e1; + var b : float1e2; + var c : float2e2; + + var f : float24e8; + var d : float53e11; + + a := 0x0.0e0f2e1; //Error + b := 0x0.0e0f1e2; //Error + c := 0x0.0e0f2e2; //No Error + + f := 0x0.000004e-32f24e8; //Error + f := 0x0.000008e-32f24e8; //No Error + f := 0x1.0e32f24e8; //Error + f := 0x0.8e32f24e8; //No Error + + f := 0x1.ffffffe0f24e8; //Error + f := -0x1.ffffffe0f24e8; //Error + f := 0x0.ffffffe0f24e8; //No Error + + d := 0x0.0000000000002e-256f53e11; //Error + d := 0x0.0000000000004e-256f53e11; //No Error + d := 0x1.0e256f53e11; //Error + d := 0x0.8e256f53e11; //No Error + + d := 0x3.fffffffffffffe0f53e11; //Error + d := -0x3.fffffffffffffe0f53e11; //Error + d := 0x1.fffffffffffffe0f53e11; //No Error +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax3.bpl.expect boogie-2.4.1+dfsg/Test/floats/ConstSyntax3.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/ConstSyntax3.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/ConstSyntax3.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,11 @@ +ConstSyntax3.bpl(12,8): error: incorrectly formatted floating point, Exponent size must be greater than 1 +ConstSyntax3.bpl(13,8): error: incorrectly formatted floating point, Significand size must be greater than 1 +ConstSyntax3.bpl(16,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 8 +ConstSyntax3.bpl(18,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 8 +ConstSyntax3.bpl(21,8): error: incorrectly formatted floating point, The given significand cannot fit in the bit size 23 +ConstSyntax3.bpl(22,8): error: incorrectly formatted floating point, The given significand cannot fit in the bit size 23 +ConstSyntax3.bpl(25,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 11 +ConstSyntax3.bpl(27,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 11 +ConstSyntax3.bpl(30,8): error: incorrectly formatted floating point, The given significand cannot fit in the bit size 52 +ConstSyntax3.bpl(31,8): error: incorrectly formatted floating point, The given significand cannot fit in the bit size 52 +10 parse errors detected in ConstSyntax3.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/CorrectTypeConv.bpl boogie-2.4.1+dfsg/Test/floats/CorrectTypeConv.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/CorrectTypeConv.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/CorrectTypeConv.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,30 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_INT(int) returns (float24e8); +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_REAL(real) returns (float24e8); +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_BV32(bv32) returns (float24e8); +function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_BV64(bv64) returns (float53e11); +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_FLOAT64(float53e11) returns (float24e8); +function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_FLOAT32(float24e8) returns (float53e11); + +procedure main() returns () { + var i : int; + var r : real; + var f32 : float24e8; + var f64 : float53e11; + + f32 := TO_FLOAT32_INT(5); + f32 := TO_FLOAT32_REAL(5.0); + + f32 := TO_FLOAT32_BV32(0bv32); + f64 := TO_FLOAT64_BV64(0bv64); + + f32 := TO_FLOAT32_FLOAT64(0x0.0e0f53e11); + f64 := TO_FLOAT64_FLOAT32(0x0.0e0f24e8); + + f32 := TO_FLOAT32_FLOAT64(f64); + f64 := TO_FLOAT64_FLOAT32(f32); + + return; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/CorrectTypeConv.bpl.expect boogie-2.4.1+dfsg/Test/floats/CorrectTypeConv.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/CorrectTypeConv.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/CorrectTypeConv.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Equal1.bpl boogie-2.4.1+dfsg/Test/floats/Equal1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Equal1.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/Equal1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,15 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type float = float24e8; + +function {:builtin "fp.isNaN"} isNaN(float) returns (bool); +function {:inline} id(f:float) returns (float) {f} + +procedure Main() +{ + var nan1, nan2: float; + assume isNaN(nan1); + nan2 := id(nan1); + assert !isNaN(nan2); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Equal1.bpl.expect boogie-2.4.1+dfsg/Test/floats/Equal1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Equal1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/Equal1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,5 @@ +Equal1.bpl(14,3): Error BP5001: This assertion might not hold. +Execution trace: + Equal1.bpl(12,3): anon0 + +Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Equal2.bpl boogie-2.4.1+dfsg/Test/floats/Equal2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Equal2.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/Equal2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,14 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type float = float24e8; + +function {:builtin "fp.eq"} FEQ(float,float) returns (bool); + +procedure Main() +{ + assert 0x0.0e0f24e8 != -0x0.0e0f24e8; + assert FEQ(0x0.0e0f24e8,-0x0.0e0f24e8); + assert 0+oo24e8 != 0-oo24e8; + assert !FEQ(0+oo24e8,0-oo24e8); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Equal2.bpl.expect boogie-2.4.1+dfsg/Test/floats/Equal2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Equal2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/Equal2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/EvaluateSignBit.bpl boogie-2.4.1+dfsg/Test/floats/EvaluateSignBit.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/EvaluateSignBit.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/EvaluateSignBit.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,15 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure foo() +{ + var a : float53e11; + var b : float53e11; + var c : float53e11; + a := 0x3.2e1f53e11; //50.0 + b := 0x1.fd70a3d70a3d7e0f53e11; //1.99 + c := -0x1.fd70a3d70a3d7e0f53e11; //-1.99 + b := (b * a) / a; + c := (c * a) / a; + assert (b > 0x0.0e0f53e11); //b should be positive + assert (c < 0x0.0e0f53e11); //c should be negative +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/EvaluateSignBit.bpl.expect boogie-2.4.1+dfsg/Test/floats/EvaluateSignBit.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/EvaluateSignBit.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/EvaluateSignBit.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float0.bpl boogie-2.4.1+dfsg/Test/floats/float0.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float0.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float0.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure foo(x : real) returns (r : float8e24) -{ - r := 15; // Error - r := 15.0; // Error - r := 0e1f22e8; // Error - r := 1e0f23e8; // Error - r := x; // Error - r := 1e0f23e8 + 1e0f23e8; // Error - r := 1e0f24e8 + 1e0f23e8; // Error - - return; -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float0.bpl.expect boogie-2.4.1+dfsg/Test/floats/float0.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float0.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float0.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -float0.bpl(5,1): Error: mismatched types in assignment command (cannot assign int to float8e24) -float0.bpl(6,1): Error: mismatched types in assignment command (cannot assign real to float8e24) -float0.bpl(7,1): Error: mismatched types in assignment command (cannot assign float22e8 to float8e24) -float0.bpl(8,1): Error: mismatched types in assignment command (cannot assign float23e8 to float8e24) -float0.bpl(9,1): Error: mismatched types in assignment command (cannot assign real to float8e24) -float0.bpl(10,1): Error: mismatched types in assignment command (cannot assign float23e8 to float8e24) -float0.bpl(11,15): Error: invalid argument types (float24e8 and float23e8) to binary operator + -7 type checking errors detected in float0.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float10.bpl boogie-2.4.1+dfsg/Test/floats/float10.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float10.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float10.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_REAL(real) returns (float53e11); - -procedure double_range_true() returns () { - var x : float53e11; - havoc x; - if (x >= TO_FLOAT64_REAL(-1e307) && x <= TO_FLOAT64_REAL(1e307)) { - assert(x==x); - } -} - -procedure double_range_false() returns () { - var x : float53e11; - havoc x; - assert(x==x); -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float10.bpl.expect boogie-2.4.1+dfsg/Test/floats/float10.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float10.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float10.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -float10.bpl(17,2): Error BP5001: This assertion might not hold. -Execution trace: - float10.bpl(16,2): anon0 - -Boogie program verifier finished with 1 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float11.bpl boogie-2.4.1+dfsg/Test/floats/float11.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float11.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float11.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_INT(int) returns (float24e8); -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_REAL(real) returns (float24e8); - -procedure main() returns () { - var tick : float24e8; - var time : float24e8; - var i: int; - - tick := TO_FLOAT32_INT(1)/TO_FLOAT32_INT(10); - time := TO_FLOAT32_INT(0); - - i := 0; - while (i < 10) - { - time := time + tick; - i := i + 1; - } - assert time == TO_FLOAT32_INT(1); -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float11.bpl.expect boogie-2.4.1+dfsg/Test/floats/float11.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float11.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float11.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -float11.bpl(21,2): Error BP5001: This assertion might not hold. -Execution trace: - float11.bpl(12,7): anon0 - float11.bpl(16,2): anon3_LoopHead - float11.bpl(16,2): anon3_LoopDone - -Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float12.bpl boogie-2.4.1+dfsg/Test/floats/float12.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float12.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float12.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_FLOAT32(float24e8) returns (float53e11); -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_FLOAT64(float53e11) returns (float24e8); -function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_INT(int) returns (float53e11); -function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_REAL(real) returns (float53e11); - -procedure main() returns () { - var x : float53e11; - var y : float24e8; - - x := TO_FLOAT64_REAL(1e20)+TO_FLOAT64_INT(1); - y := TO_FLOAT32_FLOAT64(x); - assert x != TO_FLOAT64_FLOAT32(y); -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float12.bpl.expect boogie-2.4.1+dfsg/Test/floats/float12.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float12.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float12.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float13.bpl boogie-2.4.1+dfsg/Test/floats/float13.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float13.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float13.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_INT(int) returns (float24e8); -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_REAL(real) returns (float24e8); -function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_INT(int) returns (float53e11); -function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_REAL(real) returns (float53e11); - -procedure main() returns () { - var f : float24e8; - var fc : float24e8; - var d : float53e11; - var dc : float53e11; - - f := 2097152e129f24e8; - fc := TO_FLOAT32_INT(5); - assert(f == fc); - - f := -0e126f24e8; - fc := TO_FLOAT32_REAL(-0.5); - assert(f == fc); - - f := 1048576e128f24e8; - fc := TO_FLOAT32_REAL(2.25); - assert(f == fc); - - d := 1125899906842624e1025f53e11; - dc := TO_FLOAT64_INT(5); - assert(d == dc); - - d := 562949953421312e1024f53e11; - dc := TO_FLOAT64_REAL(2.25); - assert(d == dc); -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float13.bpl.expect boogie-2.4.1+dfsg/Test/floats/float13.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float13.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float13.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float14.bpl boogie-2.4.1+dfsg/Test/floats/float14.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float14.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float14.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure main() returns () { - var f : float24e8; - var d : float53e11; - - f := 0e-1f24e8; //Error - f := 0e256f24e8; //Error - f := 0e255f24e8; //No Error - - f := 8388608e0f24e8; //Error - f := -8388608e0f24e8; //Error - f := 8388607e0f24e8; //No Error - - d := 0e-1f53e11; //Error - d := 0e2048f53e11; //Error - d := 0e2047f53e11; //No Error - - d := 4503599627370496e0f53e11; //Error - d := -4503599627370496e0f53e11; //Error - d := 4503599627370495e0f53e11; //No Error -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float14.bpl.expect boogie-2.4.1+dfsg/Test/floats/float14.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float14.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float14.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -float14.bpl(7,7): error: incorrectly formatted floating point, The given exponent -1 cannot fit in the bit size 8 -float14.bpl(8,7): error: incorrectly formatted floating point, The given exponent 256 cannot fit in the bit size 8 -float14.bpl(11,7): error: incorrectly formatted floating point, The given significand 8388608 cannot fit in the bit size 24 -float14.bpl(12,7): error: incorrectly formatted floating point, The given significand 8388608 cannot fit in the bit size 24 -float14.bpl(15,7): error: incorrectly formatted floating point, The given exponent -1 cannot fit in the bit size 11 -float14.bpl(16,7): error: incorrectly formatted floating point, The given exponent 2048 cannot fit in the bit size 11 -float14.bpl(19,7): error: incorrectly formatted floating point, The given significand 4503599627370496 cannot fit in the bit size 53 -float14.bpl(20,7): error: incorrectly formatted floating point, The given significand 4503599627370496 cannot fit in the bit size 53 -8 parse errors detected in float14.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float1.bpl boogie-2.4.1+dfsg/Test/floats/float1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float1.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float1.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure foo(x : float24e8) returns (r : float24e8) -{ - r := 0e128f24e8; - r := 1e127f24e8; - r := x; - r := x + 1e127f24e8; - r := 0e127f24e8 + 0e127f24e8; - assert(r == 0e128f24e8); - - return; -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float1.bpl.expect boogie-2.4.1+dfsg/Test/floats/float1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float1.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float2.bpl boogie-2.4.1+dfsg/Test/floats/float2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float2.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float2.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure foo(x : float11e5) returns(r : float24e8) { - var y : float53e11; - var z : float113e15; - - r := x; // Error - r := y; // Error - r := z; // Error - y := x; // Error - y := z; // Error - z := x; // Error - - return; -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float2.bpl.expect boogie-2.4.1+dfsg/Test/floats/float2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float2.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -float2.bpl(7,1): Error: mismatched types in assignment command (cannot assign float11e5 to float24e8) -float2.bpl(8,1): Error: mismatched types in assignment command (cannot assign float53e11 to float24e8) -float2.bpl(9,1): Error: mismatched types in assignment command (cannot assign float113e15 to float24e8) -float2.bpl(10,1): Error: mismatched types in assignment command (cannot assign float11e5 to float53e11) -float2.bpl(11,1): Error: mismatched types in assignment command (cannot assign float113e15 to float53e11) -float2.bpl(12,1): Error: mismatched types in assignment command (cannot assign float11e5 to float113e15) -6 type checking errors detected in float2.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float3.bpl boogie-2.4.1+dfsg/Test/floats/float3.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float3.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float3.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure main() returns () { - var x : float24e8; - var y : float24e8; - var z : float24e8; - - z := x + y; - z := x - y; - z := x * y; - assume(y != 0e0f24e8); - z := x / y; - - z := (0e127f24e8 + 0e127f24e8 + 0e0f24e8); - assert(z == 0e128f24e8); - - z := 0e128f24e8 - 0e127f24e8; - assert(z == 0e127f24e8); - - z := 0e127f24e8 * 0e127f24e8; - assert(z == 0e127f24e8); - - z := 0e127f24e8 / 0e127f24e8; - assert(z == 0e127f24e8); - - return; -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float3.bpl.expect boogie-2.4.1+dfsg/Test/floats/float3.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float3.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float3.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float4.bpl boogie-2.4.1+dfsg/Test/floats/float4.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float4.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float4.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure foo() returns (r : float8e24) { - var d : float53e11; - - r := 0NaN8e24; - r := 0nan8e24; - r := 0+oo8e24; - r := 0-oo8e24; - r := -5e255f8e24; - - d := 0NaN53e11; - d := 0nan53e11; - d := 0+oo53e11; - d := 0-oo53e11; - d := -200e2000f53e11; - - return; -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float4.bpl.expect boogie-2.4.1+dfsg/Test/floats/float4.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float4.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float4.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float5.bpl boogie-2.4.1+dfsg/Test/floats/float5.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float5.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float5.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_INT(int) returns (float23e8); -function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_REAL(real) returns (float23e8); -function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_BV31(bv31) returns (float23e8); -function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_BV32(bv32) returns (float23e8); -function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_FLOAT824(float24e8) returns (float24e8); -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT824_FLOAT823(float23e8) returns (float24e8); - -procedure foo(x : float24e8) returns (r : float24e8) { - r := TO_FLOAT823_INT(5); // Error - r := TO_FLOAT823_REAL(5.0); // Error - r := TO_FLOAT823_BV31(0bv31); // Error - r := TO_FLOAT823_BV32(0bv32); // Error - r := TO_FLOAT823_FLOAT824(0e0f24e8); // Error - r := TO_FLOAT824_FLOAT823(0e0f23e8); // Error - - r := TO_FLOAT823_FLOAT824(x); // Error - r := TO_FLOAT824_FLOAT823(x); // Error - - return; -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float5.bpl.expect boogie-2.4.1+dfsg/Test/floats/float5.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float5.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float5.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -float5.bpl(12,1): Error: mismatched types in assignment command (cannot assign float23e8 to float24e8) -float5.bpl(13,1): Error: mismatched types in assignment command (cannot assign float23e8 to float24e8) -float5.bpl(14,1): Error: mismatched types in assignment command (cannot assign float23e8 to float24e8) -float5.bpl(15,1): Error: mismatched types in assignment command (cannot assign float23e8 to float24e8) -float5.bpl(20,27): Error: invalid type for argument 0 in application of TO_FLOAT824_FLOAT823: float24e8 (expected: float23e8) -5 type checking errors detected in float5.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float6.bpl boogie-2.4.1+dfsg/Test/floats/float6.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float6.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float6.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_INT(int) returns (float24e8); -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_REAL(real) returns (float24e8); -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_BV32(bv32) returns (float24e8); -function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_BV64(bv64) returns (float53e11); -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_FLOAT64(float53e11) returns (float24e8); -function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_FLOAT32(float24e8) returns (float53e11); - -procedure main() returns () { - var i : int; - var r : real; - var f32 : float24e8; - var f64 : float53e11; - - f32 := TO_FLOAT32_INT(5); - f32 := TO_FLOAT32_REAL(5.0); - - f32 := TO_FLOAT32_BV32(0bv32); - f64 := TO_FLOAT64_BV64(0bv64); - - f32 := TO_FLOAT32_FLOAT64(0e0f53e11); - f64 := TO_FLOAT64_FLOAT32(0e0f24e8); - - f32 := TO_FLOAT32_FLOAT64(f64); - f64 := TO_FLOAT64_FLOAT32(f32); - - return; -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float6.bpl.expect boogie-2.4.1+dfsg/Test/floats/float6.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float6.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float6.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float7.bpl boogie-2.4.1+dfsg/Test/floats/float7.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float7.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float7.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure main() returns () { - var x : float53e11; - var y : float53e11; - var z : float53e11; - var r : float53e11; - x := 0e1063f53e11; - y := x + 0e1023f53e11; - z := x - 0e1023f53e11; - r := y - z; - assert r == 0e1024f53e11; -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float7.bpl.expect boogie-2.4.1+dfsg/Test/floats/float7.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float7.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float7.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float8.bpl boogie-2.4.1+dfsg/Test/floats/float8.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float8.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float8.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure main() returns () { - var x : float24e8; - var y : float24e8; - var z : float24e8; - var r : float24e8; - x := 0e167f24e8; - y := x + 0e127f24e8; - z := x - 0e127f24e8; - r := y - z; - assert r == 0e128f24e8; -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float8.bpl.expect boogie-2.4.1+dfsg/Test/floats/float8.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float8.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float8.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -float8.bpl(12,2): Error BP5001: This assertion might not hold. -Execution trace: - float8.bpl(8,4): anon0 - -Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float9.bpl boogie-2.4.1+dfsg/Test/floats/float9.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float9.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float9.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -// RUN: %boogie -proverWarnings:1 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_INT(int) returns (float24e8); -function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_REAL(real) returns (float24e8); - -procedure main() returns () { - var x : float24e8; - var y : float24e8; - var z : float24e8; - var r : float24e8; - x := TO_FLOAT32_REAL(1e7); - y := x + TO_FLOAT32_INT(1); - z := x - TO_FLOAT32_INT(1); - r := y - z; - assert r == TO_FLOAT32_INT(2); -} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float9.bpl.expect boogie-2.4.1+dfsg/Test/floats/float9.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/float9.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/float9.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/git-issue80.bpl boogie-2.4.1+dfsg/Test/floats/git-issue80.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/git-issue80.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/git-issue80.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,12 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type Ref; +var Heap: HeapType; +type Field A B; +type HeapType = [Ref, Field A B]B; + +procedure mfl(one: float23e11, two: float23e11) returns () +{ + assert two == one; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/git-issue80.bpl.expect boogie-2.4.1+dfsg/Test/floats/git-issue80.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/git-issue80.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/git-issue80.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,5 @@ +git-issue80.bpl(11,3): Error BP5001: This assertion might not hold. +Execution trace: + git-issue80.bpl(11,3): anon0 + +Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Havoc.bpl boogie-2.4.1+dfsg/Test/floats/Havoc.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Havoc.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/Havoc.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,20 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_REAL(real) returns (float53e11); + +procedure double_range_true() returns () { + var x : float53e11; + havoc x; + if (x >= TO_FLOAT64_REAL(-1e307) && x <= TO_FLOAT64_REAL(1e307)) { + assert(EQ(x,x)); + } +} + +procedure double_range_false() returns () { + var x : float53e11; + havoc x; + assert(EQ(x,x)); +} + +function {:builtin "fp.eq"} EQ(float53e11,float53e11) returns (bool); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Havoc.bpl.expect boogie-2.4.1+dfsg/Test/floats/Havoc.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/Havoc.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/Havoc.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,5 @@ +Havoc.bpl(17,3): Error BP5001: This assertion might not hold. +Execution trace: + Havoc.bpl(16,3): anon0 + +Boogie program verifier finished with 1 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/IncorrectTypeConv.bpl boogie-2.4.1+dfsg/Test/floats/IncorrectTypeConv.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/IncorrectTypeConv.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/IncorrectTypeConv.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,23 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_INT(int) returns (float23e8); +function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_REAL(real) returns (float23e8); +function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_BV31(bv31) returns (float23e8); +function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_BV32(bv32) returns (float23e8); +function {:builtin "(_ to_fp 8 23) RNE"} TO_FLOAT823_FLOAT824(float24e8) returns (float24e8); +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT824_FLOAT823(float23e8) returns (float24e8); + +procedure foo(x : float24e8) returns (r : float24e8) { + r := TO_FLOAT823_INT(5); // Error + r := TO_FLOAT823_REAL(5.0); // Error + r := TO_FLOAT823_BV31(0bv31); // Error + r := TO_FLOAT823_BV32(0bv32); // Error + r := TO_FLOAT823_FLOAT824(0x0.0e0f24e8); // Error + r := TO_FLOAT824_FLOAT823(0x0.0e0f23e8); // Error + + r := TO_FLOAT823_FLOAT824(x); // Error + r := TO_FLOAT824_FLOAT823(x); // Error + + return; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/IncorrectTypeConv.bpl.expect boogie-2.4.1+dfsg/Test/floats/IncorrectTypeConv.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/IncorrectTypeConv.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/IncorrectTypeConv.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,6 @@ +IncorrectTypeConv.bpl(12,2): Error: mismatched types in assignment command (cannot assign float23e8 to float24e8) +IncorrectTypeConv.bpl(13,2): Error: mismatched types in assignment command (cannot assign float23e8 to float24e8) +IncorrectTypeConv.bpl(14,2): Error: mismatched types in assignment command (cannot assign float23e8 to float24e8) +IncorrectTypeConv.bpl(15,2): Error: mismatched types in assignment command (cannot assign float23e8 to float24e8) +IncorrectTypeConv.bpl(20,28): Error: invalid type for argument 0 in application of TO_FLOAT824_FLOAT823: float24e8 (expected: float23e8) +5 type checking errors detected in IncorrectTypeConv.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RemovedZeroSpecialValue.bpl boogie-2.4.1+dfsg/Test/floats/RemovedZeroSpecialValue.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RemovedZeroSpecialValue.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/RemovedZeroSpecialValue.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,8 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure foo() returns (r : float24e8) { + var d : float53e11; + + d := 0+zero53e11; + d := 0-zero53e11; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RemovedZeroSpecialValue.bpl.expect boogie-2.4.1+dfsg/Test/floats/RemovedZeroSpecialValue.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RemovedZeroSpecialValue.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/RemovedZeroSpecialValue.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,3 @@ +RemovedZeroSpecialValue.bpl(6,9): Error: undeclared identifier: zero53e11 +RemovedZeroSpecialValue.bpl(7,9): Error: undeclared identifier: zero53e11 +2 name resolution errors detected in RemovedZeroSpecialValue.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundingMode1.bpl boogie-2.4.1+dfsg/Test/floats/RoundingMode1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundingMode1.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/RoundingMode1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,31 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +type float32 = float24e8; + +function {:builtin "fp.add"} ADD(rmode, float32, float32) returns (float32); +function {:builtin "fp.eq"} EQ(float32, float32) returns (bool); + +procedure {:inline 1} roundingTest(f1: float32, f2: float32) +{ + var rm: rmode; + var rneSum, rtnSum: float32; + + rm := RNE; + rneSum := f1 + f2; + assert EQ(rneSum,0x1.000002e0f24e8); + + rm := RTN; + rtnSum := ADD(rm,f1,f2); + assert EQ(rtnSum,0x1.0e0f24e8); +} + +procedure Main() +{ + var f1,f2: float32; + + call roundingTest(0x1.0e0f24e8, 0x1.8e-6f24e8); + + assume 0x0.ffffffe0f24e8 < f1 && f1 < 0x1.000002e0f24e8; + assume 0x1.7ffffee-6f24e8 < f2 && f2 < 0x1.800002e-6f24e8; + call roundingTest(f1, f2); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundingMode1.bpl.expect boogie-2.4.1+dfsg/Test/floats/RoundingMode1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundingMode1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/RoundingMode1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundingMode2.bpl boogie-2.4.1+dfsg/Test/floats/RoundingMode2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundingMode2.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/RoundingMode2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,55 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +type float64 = float53e11; + +function {:builtin "(_ fp.to_sbv 64)"} D2I(rmode, float64) returns (bv64); +function {:builtin "(_ to_fp 11 53)"} R2D(rmode, real) returns (float64); + +procedure Main() +{ + var rm: rmode; + + rm := RNE; + assert D2I(rm,R2D(RNE,2.3)) == 2bv64; + assert D2I(rm,R2D(RNE,2.5)) == 2bv64; + assert D2I(rm,R2D(RNE,3.5)) == 4bv64; + assert D2I(rm,R2D(RNE,-2.3)) == 18446744073709551614bv64; + assert D2I(rm,R2D(RNE,-2.5)) == 18446744073709551614bv64; + assert D2I(rm,R2D(RNE,-3.5)) == 18446744073709551612bv64; + + rm := RTN; + assert D2I(rm,R2D(RNE,2.3)) == 2bv64; + assert D2I(rm,R2D(RNE,2.5)) == 2bv64; + assert D2I(rm,R2D(RNE,3.5)) == 3bv64; + assert D2I(rm,R2D(RNE,-2.3)) == 18446744073709551613bv64; + assert D2I(rm,R2D(RNE,-2.5)) == 18446744073709551613bv64; + assert D2I(rm,R2D(RNE,-3.5)) == 18446744073709551612bv64; + + rm := RNA; + assert D2I(rm,R2D(RNE,2.3)) == 2bv64; + assert D2I(rm,R2D(RNE,2.5)) == 3bv64; + assert D2I(rm,R2D(RNE,-2.3)) == 18446744073709551614bv64; + assert D2I(rm,R2D(RNE,-2.5)) == 18446744073709551613bv64; + + // These assertions should pass. + // They were broken in a previous version of Z3 (4.5.0), + // but work correctly with Z3 4.8.4. + assert D2I(rm,R2D(RNE,3.5)) == 4bv64; + assert D2I(rm,R2D(RNE,-3.5)) == 18446744073709551612bv64; + + rm := RTP; + assert D2I(rm,R2D(RNE,2.3)) == 3bv64; + assert D2I(rm,R2D(RNE,2.5)) == 3bv64; + assert D2I(rm,R2D(RNE,3.5)) == 4bv64; + assert D2I(rm,R2D(RNE,-2.3)) == 18446744073709551614bv64; + assert D2I(rm,R2D(RNE,-2.5)) == 18446744073709551614bv64; + assert D2I(rm,R2D(RNE,-3.5)) == 18446744073709551613bv64; + + rm := RTZ; + assert D2I(rm,R2D(RNE,2.3)) == 2bv64; + assert D2I(rm,R2D(RNE,2.5)) == 2bv64; + assert D2I(rm,R2D(RNE,3.5)) == 3bv64; + assert D2I(rm,R2D(RNE,-2.3)) == 18446744073709551614bv64; + assert D2I(rm,R2D(RNE,-2.5)) == 18446744073709551614bv64; + assert D2I(rm,R2D(RNE,-3.5)) == 18446744073709551613bv64; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundingMode2.bpl.expect boogie-2.4.1+dfsg/Test/floats/RoundingMode2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundingMode2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/RoundingMode2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundoffError.bpl boogie-2.4.1+dfsg/Test/floats/RoundoffError.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundoffError.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/RoundoffError.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,22 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_INT(int) returns (float24e8); +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_REAL(real) returns (float24e8); + +procedure main() returns () { + var tick : float24e8; + var time : float24e8; + var i: int; + + tick := TO_FLOAT32_INT(1)/TO_FLOAT32_INT(10); + time := TO_FLOAT32_INT(0); + + i := 0; + while (i < 10) + { + time := time + tick; + i := i + 1; + } + assert time == TO_FLOAT32_INT(1); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundoffError.bpl.expect boogie-2.4.1+dfsg/Test/floats/RoundoffError.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/RoundoffError.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/RoundoffError.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,7 @@ +RoundoffError.bpl(21,3): Error BP5001: This assertion might not hold. +Execution trace: + RoundoffError.bpl(12,8): anon0 + RoundoffError.bpl(16,3): anon3_LoopHead + RoundoffError.bpl(16,3): anon3_LoopDone + +Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/SpecialValues.bpl boogie-2.4.1+dfsg/Test/floats/SpecialValues.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/SpecialValues.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/SpecialValues.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,38 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure foo() returns (r : float24e8) { + var d : float53e11; + + r := 0NaN24e8; + r := 0nan24e8; + assert !FEQ(r,0NaN24e8); //NaN != NaN + r := 0+oo24e8; + r := 0-oo24e8; + r := 0x1.0e32f24e8; //Error: this is a synonym for +oo, which is disallowed + r := -0x1.0e32f24e8; //Error: this is a synonym for -oo, which is disallowed + r := -0x5.0e32f24e8; //Error: NaN values are disallowed + + d := 0NaN53e11; + d := 0nan53e11; + assert !DEQ(d,0NaN53e11); + d := 0+oo53e11; + d := 0-oo53e11; + d := 0; + d := 0x1.0e256f53e11; + d := -0x1.0e256f53e11; + d := -0x5.0e256f53e11; + + d := 0+zero53e11; //Error: there are no longer special values for +zero and -zero + d := 0-zero53e11; + + d := 0x0.0e0f53e11; + assert d == 0x0.0e0f53e11; + d := -0x0.0e0f53e11; + assert d == -0x0.0e0f53e11; + assert(DEQ(0x0.0e0f53e11, -0x0.0e0f53e11)); + + return; +} + +function {:builtin "fp.eq"} FEQ(float24e8,float24e8) returns (bool); +function {:builtin "fp.eq"} DEQ(float53e11,float53e11) returns (bool); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/SpecialValues.bpl.expect boogie-2.4.1+dfsg/Test/floats/SpecialValues.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/SpecialValues.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/SpecialValues.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,7 @@ +SpecialValues.bpl(11,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 8 +SpecialValues.bpl(12,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 8 +SpecialValues.bpl(13,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 8 +SpecialValues.bpl(21,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 11 +SpecialValues.bpl(22,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 11 +SpecialValues.bpl(23,8): error: incorrectly formatted floating point, The given exponent cannot fit in the bit size 11 +6 parse errors detected in SpecialValues.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeConvConst.bpl boogie-2.4.1+dfsg/Test/floats/TypeConvConst.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeConvConst.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/TypeConvConst.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,34 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_INT(int) returns (float24e8); +function {:builtin "(_ to_fp 8 24) RNE"} TO_FLOAT32_REAL(real) returns (float24e8); +function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_INT(int) returns (float53e11); +function {:builtin "(_ to_fp 11 53) RNE"} TO_FLOAT64_REAL(real) returns (float53e11); + +procedure main() returns () { + var f : float24e8; + var fc : float24e8; + var d : float53e11; + var dc : float53e11; + + f := 0x5.0e0f24e8; + fc := TO_FLOAT32_INT(5); + assert(f == fc); + + f := -0x0.8e0f24e8; + fc := TO_FLOAT32_REAL(-0.5); + assert(f == fc); + + f := 0x2.4e0f24e8; + fc := TO_FLOAT32_REAL(2.25); + assert(f == fc); + + d := 0x5.0e0f53e11; + dc := TO_FLOAT64_INT(5); + assert(d == dc); + + d := 0x2.4e0f53e11; + dc := TO_FLOAT64_REAL(2.25); + assert(d == dc); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeConvConst.bpl.expect boogie-2.4.1+dfsg/Test/floats/TypeConvConst.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeConvConst.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/TypeConvConst.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeMismatch1.bpl boogie-2.4.1+dfsg/Test/floats/TypeMismatch1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeMismatch1.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/TypeMismatch1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,15 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +procedure foo(x : float11e5) returns(r : float24e8) { + var y : float53e11; + var z : float113e15; + + r := x; // Error + r := y; // Error + r := z; // Error + y := x; // Error + y := z; // Error + z := x; // Error + + return; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeMismatch1.bpl.expect boogie-2.4.1+dfsg/Test/floats/TypeMismatch1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeMismatch1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/TypeMismatch1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,7 @@ +TypeMismatch1.bpl(7,2): Error: mismatched types in assignment command (cannot assign float11e5 to float24e8) +TypeMismatch1.bpl(8,2): Error: mismatched types in assignment command (cannot assign float53e11 to float24e8) +TypeMismatch1.bpl(9,2): Error: mismatched types in assignment command (cannot assign float113e15 to float24e8) +TypeMismatch1.bpl(10,2): Error: mismatched types in assignment command (cannot assign float11e5 to float53e11) +TypeMismatch1.bpl(11,2): Error: mismatched types in assignment command (cannot assign float113e15 to float53e11) +TypeMismatch1.bpl(12,2): Error: mismatched types in assignment command (cannot assign float11e5 to float113e15) +6 type checking errors detected in TypeMismatch1.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeMismatch2.bpl boogie-2.4.1+dfsg/Test/floats/TypeMismatch2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeMismatch2.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/TypeMismatch2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,21 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +type fl = float24e8; +type float = fl; +type do = float53e11; +type double = do; + + +procedure foo(x : double) returns (r : float) +{ + r := 0x2.0e0f24e8; + r := 0x1.000002e0f24e8; + r := x; //Error + r := x * 0x1.000002e0f24e8; //Error + r := x + 0x1.000002e0f24e8; //Error + r := 0x1.0e0f24e8 + 0x1.0e0f24e8; + assert(r == 0x2.0e0f24e8); + + return; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeMismatch2.bpl.expect boogie-2.4.1+dfsg/Test/floats/TypeMismatch2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/floats/TypeMismatch2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/floats/TypeMismatch2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,4 @@ +TypeMismatch2.bpl(14,2): Error: mismatched types in assignment command (cannot assign double to float) +TypeMismatch2.bpl(15,9): Error: invalid argument types (double and float24e8) to binary operator * +TypeMismatch2.bpl(16,9): Error: invalid argument types (double and float24e8) to binary operator + +3 type checking errors detected in TypeMismatch2.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/inline/inline_n_0.bpl boogie-2.4.1+dfsg/Test/inline/inline_n_0.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/inline/inline_n_0.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/inline/inline_n_0.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,17 @@ +// RUN: %boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +// inlining with n=0 + +var x : int; + +procedure {:inline 0} f() // Should not be checked on its own + modifies x; +{ + x := x + 1; +} + +procedure g() + modifies x; +{ + call f(); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/inline/inline_n_0.bpl.expect boogie-2.4.1+dfsg/Test/inline/inline_n_0.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/inline/inline_n_0.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/inline/inline_n_0.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/allocator.bpl boogie-2.4.1+dfsg/Test/linear/allocator.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/allocator.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/allocator.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure A({:linear_in "tid"} i': int) returns ({:linear "tid"} i: int); - ensures i == i'; - -procedure B({:linear_in "tid"} i': int) returns ({:linear "tid"} i: int) -{ - i := i'; - call i := A(i); - assert false; -} - diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/allocator.bpl.expect boogie-2.4.1+dfsg/Test/linear/allocator.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/allocator.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/allocator.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -allocator.bpl(10,3): Error BP5001: This assertion might not hold. -Execution trace: - allocator.bpl(8,5): anon0 - -Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/async-bug.bpl boogie-2.4.1+dfsg/Test/linear/async-bug.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/async-bug.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/async-bug.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -const GcTid:int; - -procedure {:yields} {:layer 100} Initialize({:linear "tid"} tid:int) -requires{:layer 100} tid == GcTid; -{ - yield; - assert{:layer 100} tid == GcTid; - - call GarbageCollect(tid); - - yield; - assert{:layer 100} tid == GcTid; - - async call GarbageCollect(tid); - - yield; - assert{:layer 100} tid == GcTid; - - async call GarbageCollect(tid); - - yield; - assert{:layer 100} tid == GcTid; - - yield; - assert{:layer 100} tid == GcTid; -} - -procedure {:yields} {:layer 100} GarbageCollect({:linear "tid"} tid:int) -requires{:layer 100} tid == GcTid; -{ - yield; - assert{:layer 100} tid == GcTid; -} - diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/async-bug.bpl.expect boogie-2.4.1+dfsg/Test/linear/async-bug.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/async-bug.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/async-bug.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -async-bug.bpl(21,30): Error: unavailable source for a linear read -async-bug.bpl(28,0): Error: Input variable tid must be available at a return -2 type checking errors detected in async-bug.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/bug.bpl boogie-2.4.1+dfsg/Test/linear/bug.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/bug.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/bug.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -function {:builtin "MapConst"} MapConstBool(bool) : [int]bool; -function {:inline} {:linear ""} LinearIntDistinctness(x:int) : [int]bool { MapConstBool(false)[x := true] } - -var {:linear ""} g:int; - -procedure A() -{ -} - -procedure B() -{ - call A(); - assert false; -} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/bug.bpl.expect boogie-2.4.1+dfsg/Test/linear/bug.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/bug.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/bug.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -bug.bpl(15,3): Error BP5001: This assertion might not hold. -Execution trace: - bug.bpl(14,3): anon0 - -Boogie program verifier finished with 1 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f1.bpl boogie-2.4.1+dfsg/Test/linear/f1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f1.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/f1.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -function {:builtin "MapConst"} mapconstbool(bool) : [int]bool; -const {:existential true} b0: bool; -const {:existential true} b1: bool; -const {:existential true} b2: bool; -const {:existential true} b3: bool; -const {:existential true} b4: bool; -const {:existential true} b5: bool; -const {:existential true} b6: bool; -const {:existential true} b7: bool; -const {:existential true} b8: bool; - -axiom(b0); -axiom(b1); -axiom(b2); -axiom(b3); -axiom(b4); -axiom(b5); -axiom(!b6); -axiom(!b7); -axiom(b8); - -procedure main({:linear_in "1"} x_in: [int]bool) - requires b0 ==> x_in == mapconstbool(true); - requires b1 ==> x_in != mapconstbool(false); -{ - var {:linear "1"} x: [int] bool; - x := x_in; - - call foo(x); - - assert b6 ==> x == mapconstbool(true); - assert b7 ==> x != mapconstbool(false); - assert b8 ==> x == mapconstbool(false); -} - -procedure foo({:linear_in "1"} x_in: [int]bool) - requires b2 ==> x_in == mapconstbool(true); - requires b3 ==> x_in != mapconstbool(false); -{ - var {:linear "1"} x: [int] bool; - x := x_in; - - assert b4 ==> x == mapconstbool(true); - assert b5 ==> x != mapconstbool(false); - -} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f1.bpl.expect boogie-2.4.1+dfsg/Test/linear/f1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f1.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/f1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -f1.bpl(35,4): Error BP5001: This assertion might not hold. -Execution trace: - f1.bpl(29,6): anon0 - -Boogie program verifier finished with 1 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f2.bpl boogie-2.4.1+dfsg/Test/linear/f2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f2.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/f2.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -function {:builtin "MapConst"} mapconstbool(bool) : [int]bool; -function {:builtin "MapOr"} mapunion([int]bool, [int]bool) : [int]bool; - -procedure Split({:linear_in "1"} xls: [int]bool) returns ({:linear "1"} xls1: [int]bool, {:linear "1"} xls2: [int]bool); -ensures xls == mapunion(xls1, xls2) && xls1 != mapconstbool(false) && xls2 != mapconstbool(false); - -procedure Allocate() returns ({:linear "1"} x: [int]bool); - -procedure main() -{ - var {:linear "1"} x: [int] bool; - var {:linear "1"} x1: [int] bool; - var {:linear "1"} x2: [int] bool; - - call x := Allocate(); - assume x == mapconstbool(true); - - call x1, x2 := Split(x); - assert false; -} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f2.bpl.expect boogie-2.4.1+dfsg/Test/linear/f2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f2.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/f2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -f2.bpl(21,4): Error BP5001: This assertion might not hold. -Execution trace: - f2.bpl(17,4): anon0 - -Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f3.bpl boogie-2.4.1+dfsg/Test/linear/f3.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f3.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/f3.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -procedure A() {} - -procedure B({:linear_in ""} tid:int) returns({:linear ""} tid':int) -{ - tid' := tid; - call A(); -} - diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f3.bpl.expect boogie-2.4.1+dfsg/Test/linear/f3.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/f3.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/f3.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 2 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/list.bpl boogie-2.4.1+dfsg/Test/linear/list.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/list.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/list.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory -doModSetAnalysis "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -type X; -function {:builtin "MapConst"} MapConstBool(bool) : [X]bool; -function {:builtin "MapOr"} MapOr([X]bool, [X]bool) : [X]bool; - -function {:inline} {:linear "Mem"} MemCollector(xs: [X]bool) : [X]bool -{ - xs -} - -var head: X; -var tail: X; -var {:linear "Mem"} D: [X]bool; -var Next:[X]X; -const nil: X; - -procedure malloc() returns (x: X, {:linear "Mem"} M: [X]bool); -ensures M == MapConstBool(false)[x := true]; - -procedure Join({:linear_in "Mem"} A: [X]bool); -modifies D; -ensures MapOr(old(D), A) == D; - -procedure one() -requires D[head] && D[tail]; -requires (forall d: X :: {D[d]} D[d] ==> D[Next[d]] || d == tail); -ensures D[head] && D[tail]; -ensures (forall d: X :: {D[d]} D[d] ==> D[Next[d]] || d == tail); -ensures head != tail; -{ - var x: X; - var {:linear "Mem"} M: [X]bool; - - call x, M := malloc(); - call Join(M); - Next[tail] := x; - tail := x; - Next[tail] := nil; -} - -procedure two() -requires head != tail; -requires D[head] && D[tail]; -requires (forall d: X :: {D[d]} D[d] ==> D[Next[d]] || d == tail); -ensures (forall d: X :: {D[d]} D[d] ==> D[Next[d]] || d == tail); -ensures D[head] && D[tail]; -{ - head := Next[head]; -} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/list.bpl.expect boogie-2.4.1+dfsg/Test/linear/list.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/list.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/list.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 2 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/typecheck.bpl boogie-2.4.1+dfsg/Test/linear/typecheck.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/typecheck.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/typecheck.bpl 1970-01-01 00:00:00.000000000 +0000 @@ -1,118 +0,0 @@ -// RUN: %boogie -noinfer -typeEncoding:m -useArrayTheory "%s" > "%t" -// RUN: %diff "%s.expect" "%t" -type X; - -procedure A() -{ - var {:linear "A"} a: X; - var {:linear "A"} b: int; -} - -procedure B() -{ - var {:linear "B"} a: X; - var {:linear "B"} b: [X]bool; -} - -procedure C() -{ - var {:linear "C"} a: X; - var {:linear "C"} c: [X]int; -} - -function f(X): X; - -procedure {:yields} {:layer 1} D() -{ - var {:linear "D"} a: X; - var {:linear "D"} x: X; - var {:linear "D"} b: [X]bool; - var c: X; - var {:linear "D2"} d: X; - - b[a] := true; - - a := f(a); - - a := c; - - c := a; - - a := d; - - a := a; - - a, x := x, a; - - a, x := x, x; - - call a, x := E(a, x); - - call a, x := E(a, a); - - call a, x := E(a, f(a)); - - call a, x := E(a, d); - - call d, x := E(a, x); - - call a, x := E(c, x); - - call c, x := E(a, x); - - yield; - par a := F(a) | x := F(a); - yield; -} - -procedure {:yields} {:layer 1} E({:linear_in "D"} a: X, {:linear_in "D"} b: X) returns ({:linear "D"} c: X, {:linear "D"} d: X) -{ - yield; - c := a; - yield; -} - -procedure {:yields} {:layer 0} F({:linear_in "D"} a: X) returns ({:linear "D"} c: X); - -var {:linear "x"} g:int; - -procedure G(i:int) returns({:linear "x"} r:int) -{ - r := g; -} - -procedure H(i:int) returns({:linear "x"} r:int) -modifies g; -{ - g := r; -} - -procedure {:yields} {:layer 0} I({:linear_in ""} x:int) returns({:linear ""} x':int) -{ - yield; - x' := x; - yield; -} - -procedure {:yields} {:layer 0} J() -{ - yield; -} - -procedure {:yields} {:layer 1} P1({:linear_in ""} x:int) returns({:linear ""} x':int) -{ - yield; - par x' := I(x) | J(); - yield; - call x' := I(x'); - yield; -} - -procedure {:yields} {:layer 1} P2({:linear_in ""} x:int) returns({:linear ""} x':int) -{ - yield; - call x' := I(x); - yield; - par x' := I(x') | J(); - yield; -} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/typecheck.bpl.expect boogie-2.4.1+dfsg/Test/linear/typecheck.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/linear/typecheck.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/linear/typecheck.bpl.expect 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -typecheck.bpl(33,9): Error: Only simple assignment allowed on linear variable b -typecheck.bpl(35,6): Error: Only variable can be assigned to linear variable a -typecheck.bpl(37,6): Error: Only linear variable can be assigned to linear variable a -typecheck.bpl(41,6): Error: Linear variable of domain D2 cannot be assigned to linear variable of domain D -typecheck.bpl(47,9): Error: Linear variable x can occur only once in the right-hand-side of an assignment -typecheck.bpl(51,4): Error: Linear variable a can occur only once as an input parameter -typecheck.bpl(53,4): Error: Only variable can be passed to linear parameter b -typecheck.bpl(55,4): Error: The domains of formal and actual parameters must be the same -typecheck.bpl(57,4): Error: The domains of formal and actual parameters must be the same -typecheck.bpl(59,4): Error: Only a linear argument can be passed to linear parameter a -typecheck.bpl(64,4): Error: Linear variable a can occur only once as an input parameter of a parallel call -typecheck.bpl(73,0): Error: Output variable d must be available at a return -typecheck.bpl(82,0): Error: Global variable g must be available at a return -typecheck.bpl(87,7): Error: unavailable source for a linear read -typecheck.bpl(88,0): Error: Output variable r must be available at a return -15 type checking errors detected in typecheck.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/lit.site.cfg boogie-2.4.1+dfsg/Test/lit.site.cfg --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/lit.site.cfg 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/lit.site.cfg 2019-10-25 18:17:05.000000000 +0000 @@ -104,7 +104,7 @@ boogieExecutable = boogieExecutable + ' ' + boogieParams # Inform user what executable is being used -lit_config.note('Using Boogie: {}\n'.format(boogieExecutable)) +lit_config.note('Using Boogie: {}'.format(boogieExecutable)) config.substitutions.append( ('%boogie', boogieExecutable) ) @@ -127,16 +127,18 @@ lit_config.warning('Skipping solver sanity check on Windows') # Add diff tool substitution -commonDiffFlags=' --unified=3 --strip-trailing-cr --ignore-all-space' -diffExecutable = None if os.name == 'posix': - diffExecutable = 'diff' + commonDiffFlags -elif os.name == 'nt': - pydiff = quotePath( os.path.join(config.test_source_root, 'pydiff.py') ) - diffExecutable = sys.executable + ' ' + pydiff + commonDiffFlags + # use the system diff + # Note: We need to get the absolute path because otherwise lit uses a + # built-in diff that does not support all flags below. + diffExecutable = lit.util.which('diff') else: - lit_config.fatal('Unsupported platform') -lit_config.note("Using diff tool '{}'".format(diffExecutable)) + # use driver around Python's difflib + pydiff = quotePath( os.path.join(config.test_source_root, 'pydiff.py') ) + diffExecutable = sys.executable + ' ' + pydiff + +diffExecutable += ' --unified=3 --strip-trailing-cr --ignore-all-space' +lit_config.note("Using diff tool: {}".format(diffExecutable)) config.substitutions.append( ('%diff', diffExecutable )) diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/livevars/daytona_bug2_ioctl_example_2.bpl boogie-2.4.1+dfsg/Test/livevars/daytona_bug2_ioctl_example_2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/livevars/daytona_bug2_ioctl_example_2.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/livevars/daytona_bug2_ioctl_example_2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -1,4 +1,4 @@ -// RUN: %boogie -noinfer -useArrayTheory "%s" > "%t" +// RUN: %boogie -noinfer -useArrayTheory -errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" var __storm_thread_done_0 : bool; var __storm_thread_done_1 : bool; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect boogie-2.4.1+dfsg/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/livevars/daytona_bug2_ioctl_example_2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,148 +1,3 @@ daytona_bug2_ioctl_example_2.bpl(4835,5): Error BP5001: This assertion might not hold. -Execution trace: - daytona_bug2_ioctl_example_2.bpl(807,3): start#2 - daytona_bug2_ioctl_example_2.bpl(848,3): anon22_Then#2 - daytona_bug2_ioctl_example_2.bpl(853,3): anon2#2 - daytona_bug2_ioctl_example_2.bpl(881,3): inline$storm_IoAllocateIrp$0$label_8_case_1#2 - daytona_bug2_ioctl_example_2.bpl(913,3): inline$storm_IoAllocateIrp$0$anon18_Then#2 - daytona_bug2_ioctl_example_2.bpl(918,3): inline$storm_IoAllocateIrp$0$anon2#2 - daytona_bug2_ioctl_example_2.bpl(938,3): inline$storm_IoAllocateIrp$0$anon20_Then#2 - daytona_bug2_ioctl_example_2.bpl(943,3): inline$storm_IoAllocateIrp$0$anon5#2 - daytona_bug2_ioctl_example_2.bpl(963,3): inline$storm_IoAllocateIrp$0$anon22_Then#2 - daytona_bug2_ioctl_example_2.bpl(968,3): inline$storm_IoAllocateIrp$0$anon8#2 - daytona_bug2_ioctl_example_2.bpl(976,3): inline$storm_IoAllocateIrp$0$anon24_Else#2 - daytona_bug2_ioctl_example_2.bpl(1012,3): inline$storm_IoAllocateIrp$0$anon25_Then#2 - daytona_bug2_ioctl_example_2.bpl(1017,3): inline$storm_IoAllocateIrp$0$anon13#2 - daytona_bug2_ioctl_example_2.bpl(1048,3): inline$IoGetNextIrpStackLocation$0$anon3_Then#2 - daytona_bug2_ioctl_example_2.bpl(1053,3): inline$IoGetNextIrpStackLocation$0$anon2#2 - daytona_bug2_ioctl_example_2.bpl(1068,3): inline$storm_IoAllocateIrp$0$anon27_Else#2 - daytona_bug2_ioctl_example_2.bpl(1092,3): inline$storm_IoAllocateIrp$0$anon28_Then#2 - daytona_bug2_ioctl_example_2.bpl(1097,3): inline$storm_IoAllocateIrp$0$anon17#2 - daytona_bug2_ioctl_example_2.bpl(1117,3): inline$storm_IoAllocateIrp$0$label_36#2 - daytona_bug2_ioctl_example_2.bpl(1131,3): anon24_Else#2 - daytona_bug2_ioctl_example_2.bpl(1146,3): anon25_Else#2 - daytona_bug2_ioctl_example_2.bpl(1186,3): inline$IoSetNextIrpStackLocation$0$anon6_Then#2 - daytona_bug2_ioctl_example_2.bpl(1191,3): inline$IoSetNextIrpStackLocation$0$anon2#2 - daytona_bug2_ioctl_example_2.bpl(1209,3): inline$IoSetNextIrpStackLocation$0$anon8_Then#2 - daytona_bug2_ioctl_example_2.bpl(1214,3): inline$IoSetNextIrpStackLocation$0$anon5#2 - daytona_bug2_ioctl_example_2.bpl(1227,3): anon26_Else#2 - daytona_bug2_ioctl_example_2.bpl(1261,3): inline$IoGetCurrentIrpStackLocation$0$anon3_Then#2 - daytona_bug2_ioctl_example_2.bpl(1266,3): inline$IoGetCurrentIrpStackLocation$0$anon2#2 - daytona_bug2_ioctl_example_2.bpl(1281,3): anon27_Else#2 - daytona_bug2_ioctl_example_2.bpl(1308,3): anon28_Then#2 - daytona_bug2_ioctl_example_2.bpl(1313,3): anon13#2 - daytona_bug2_ioctl_example_2.bpl(1345,3): inline$myInitDriver$0$anon5_Then#2 - daytona_bug2_ioctl_example_2.bpl(1350,3): inline$myInitDriver$0$anon2#2 - daytona_bug2_ioctl_example_2.bpl(1385,3): inline$storm_KeInitializeSpinLock$0$anon3_Then#2 - daytona_bug2_ioctl_example_2.bpl(1390,3): inline$storm_KeInitializeSpinLock$0$anon2#2 - daytona_bug2_ioctl_example_2.bpl(1403,3): inline$myInitDriver$0$anon7_Else#2 - daytona_bug2_ioctl_example_2.bpl(1424,3): anon30_Else#2 - daytona_bug2_ioctl_example_2.bpl(1480,3): inline$IoGetCurrentIrpStackLocation$1$anon3_Then#2 - daytona_bug2_ioctl_example_2.bpl(1484,3): inline$IoGetCurrentIrpStackLocation$1$anon2#2 - daytona_bug2_ioctl_example_2.bpl(1497,3): inline$dispatch$0$anon4_Else#2 - daytona_bug2_ioctl_example_2.bpl(1536,3): inline$I8xDeviceControl$0$anon13_Else#2 - daytona_bug2_ioctl_example_2.bpl(1559,3): inline$I8xDeviceControl$0$anon14_Then#2 - daytona_bug2_ioctl_example_2.bpl(1564,3): inline$I8xDeviceControl$0$anon4#2 - daytona_bug2_ioctl_example_2.bpl(1577,3): inline$I8xDeviceControl$0$label_11_true#2 - daytona_bug2_ioctl_example_2.bpl(1590,3): inline$I8xDeviceControl$0$label_13_true#2 - daytona_bug2_ioctl_example_2.bpl(1598,3): inline$I8xDeviceControl$0$label_14_false#2 - daytona_bug2_ioctl_example_2.bpl(1627,3): inline$IoGetCurrentIrpStackLocation$2$anon3_Then#2 - daytona_bug2_ioctl_example_2.bpl(1631,3): inline$IoGetCurrentIrpStackLocation$2$anon2#2 - daytona_bug2_ioctl_example_2.bpl(1644,3): inline$I8xDeviceControl$0$anon16_Else#2 - daytona_bug2_ioctl_example_2.bpl(1657,3): inline$I8xDeviceControl$0$label_19_case_2#2 - daytona_bug2_ioctl_example_2.bpl(1721,3): inline$IoGetCurrentIrpStackLocation$4$anon3_Then#2 - daytona_bug2_ioctl_example_2.bpl(1725,3): inline$IoGetCurrentIrpStackLocation$4$anon2#2 - daytona_bug2_ioctl_example_2.bpl(1738,3): inline$I8xKeyboardGetSysButtonEvent$0$anon34_Else#2 - daytona_bug2_ioctl_example_2.bpl(1751,3): inline$I8xKeyboardGetSysButtonEvent$0$label_14_false#2 - daytona_bug2_ioctl_example_2.bpl(1759,3): inline$I8xKeyboardGetSysButtonEvent$0$label_15_false#2 - daytona_bug2_ioctl_example_2.bpl(1813,3): inline$storm_KeAcquireSpinLock$0$anon10_Else#2 - daytona_bug2_ioctl_example_2.bpl(1837,3): inline$storm_KeAcquireSpinLock$0$label_13_true#2 - daytona_bug2_ioctl_example_2.bpl(1845,3): inline$storm_KeAcquireSpinLock$0$anon11_Else#2 - daytona_bug2_ioctl_example_2.bpl(1869,3): inline$storm_KeAcquireSpinLock$0$anon12_Then#2 - daytona_bug2_ioctl_example_2.bpl(1874,3): inline$storm_KeAcquireSpinLock$0$anon7#2 - daytona_bug2_ioctl_example_2.bpl(1885,3): inline$storm_KeAcquireSpinLock$0$anon14_Then#2 - daytona_bug2_ioctl_example_2.bpl(1890,3): inline$storm_KeAcquireSpinLock$0$anon9#2 - daytona_bug2_ioctl_example_2.bpl(1894,3): inline$storm_KeAcquireSpinLock$0$label_1#2 - daytona_bug2_ioctl_example_2.bpl(1911,3): inline$I8xKeyboardGetSysButtonEvent$0$anon36_Else#2 - daytona_bug2_ioctl_example_2.bpl(1922,3): inline$I8xKeyboardGetSysButtonEvent$0$label_56_false#2 - daytona_bug2_ioctl_example_2.bpl(1953,3): inline$storm_IoSetCancelRoutine$0$label_7_false#2 - daytona_bug2_ioctl_example_2.bpl(2012,3): inline$storm_IoSetCancelRoutine$0$anon12_Then#2 - daytona_bug2_ioctl_example_2.bpl(2016,3): inline$storm_IoSetCancelRoutine$0$anon5#2 - daytona_bug2_ioctl_example_2.bpl(2036,3): inline$storm_IoSetCancelRoutine$0$anon14_Then#2 - daytona_bug2_ioctl_example_2.bpl(2041,3): inline$storm_IoSetCancelRoutine$0$anon8#2 - daytona_bug2_ioctl_example_2.bpl(2052,3): inline$storm_IoSetCancelRoutine$0$anon16_Then#2 - daytona_bug2_ioctl_example_2.bpl(2057,3): inline$storm_IoSetCancelRoutine$0$anon10#2 - daytona_bug2_ioctl_example_2.bpl(2064,3): inline$storm_IoSetCancelRoutine$0$label_1#2 - daytona_bug2_ioctl_example_2.bpl(2077,3): inline$I8xKeyboardGetSysButtonEvent$0$anon44_Else#2 - daytona_bug2_ioctl_example_2.bpl(2202,3): inline$I8xKeyboardGetSysButtonEvent$0$anon45_Else#2 - daytona_bug2_ioctl_example_2.bpl(2210,3): inline$I8xKeyboardGetSysButtonEvent$0$anon46_Then#2 - daytona_bug2_ioctl_example_2.bpl(2220,3): inline$I8xKeyboardGetSysButtonEvent$0$anon24#2 - daytona_bug2_ioctl_example_2.bpl(2248,3): inline$storm_IoSetCancelRoutine$1$label_7_false#2 - daytona_bug2_ioctl_example_2.bpl(2295,3): inline$storm_IoSetCancelRoutine$1$anon12_Else#2 - daytona_bug2_ioctl_example_2.bpl(2303,3): inline$storm_IoSetCancelRoutine$1$anon13_Then#2 - daytona_bug2_ioctl_example_2.bpl(2313,3): inline$storm_IoSetCancelRoutine$1$anon5#2 - daytona_bug2_ioctl_example_2.bpl(2321,3): inline$storm_IoSetCancelRoutine$1$anon14_Else#2 - daytona_bug2_ioctl_example_2.bpl(2329,3): inline$storm_IoSetCancelRoutine$1$anon15_Then#2 - daytona_bug2_ioctl_example_2.bpl(2339,3): inline$storm_IoSetCancelRoutine$1$anon8#2 - daytona_bug2_ioctl_example_2.bpl(2350,3): inline$storm_IoSetCancelRoutine$1$anon16_Then#2 - daytona_bug2_ioctl_example_2.bpl(2355,3): inline$storm_IoSetCancelRoutine$1$anon10#2 - daytona_bug2_ioctl_example_2.bpl(2363,3): inline$storm_IoSetCancelRoutine$1$label_1#2 - daytona_bug2_ioctl_example_2.bpl(2377,3): inline$I8xKeyboardGetSysButtonEvent$0$anon50_Else#2 - daytona_bug2_ioctl_example_2.bpl(2387,3): inline$I8xKeyboardGetSysButtonEvent$0$label_72_false#2 - daytona_bug2_ioctl_example_2.bpl(2409,3): inline$storm_IoMarkIrpPending$2$label_6_false#2 - daytona_bug2_ioctl_example_2.bpl(2449,3): inline$storm_IoMarkIrpPending$2$label_1#2 - daytona_bug2_ioctl_example_2.bpl(2462,3): inline$I8xKeyboardGetSysButtonEvent$0$anon51_Else#2 - daytona_bug2_ioctl_example_2.bpl(2509,3): inline$I8xKeyboardGetSysButtonEvent$0$label_59#2 - daytona_bug2_ioctl_example_2.bpl(2535,3): inline$storm_KeReleaseSpinLock$0$anon8_Else#2 - daytona_bug2_ioctl_example_2.bpl(2576,3): inline$storm_KeReleaseSpinLock$0$label_11_true#2 - daytona_bug2_ioctl_example_2.bpl(2583,3): inline$storm_KeReleaseSpinLock$0$anon9_Else#2 - daytona_bug2_ioctl_example_2.bpl(2591,3): inline$storm_KeReleaseSpinLock$0$anon10_Then#2 - daytona_bug2_ioctl_example_2.bpl(2601,3): inline$storm_KeReleaseSpinLock$0$anon5#2 - daytona_bug2_ioctl_example_2.bpl(2612,3): inline$storm_KeReleaseSpinLock$0$anon11_Then#2 - daytona_bug2_ioctl_example_2.bpl(2617,3): inline$storm_KeReleaseSpinLock$0$anon7#2 - daytona_bug2_ioctl_example_2.bpl(2621,3): inline$storm_KeReleaseSpinLock$0$label_1#2 - daytona_bug2_ioctl_example_2.bpl(2634,3): inline$I8xKeyboardGetSysButtonEvent$0$anon43_Else#2 - daytona_bug2_ioctl_example_2.bpl(2867,3): inline$I8xKeyboardGetSysButtonEvent$0$label_51_true#2 - daytona_bug2_ioctl_example_2.bpl(2910,3): inline$storm_IoCompleteRequest$2$label_6_false#2 - daytona_bug2_ioctl_example_2.bpl(2953,3): inline$storm_IoCompleteRequest$2$anon6_Else#2 - daytona_bug2_ioctl_example_2.bpl(2961,3): inline$storm_IoCompleteRequest$2$anon7_Then#2 - daytona_bug2_ioctl_example_2.bpl(2971,3): inline$storm_IoCompleteRequest$2$anon2#2 - daytona_bug2_ioctl_example_2.bpl(2975,3): inline$storm_IoCompleteRequest$2$label_1#2 - daytona_bug2_ioctl_example_2.bpl(2988,3): inline$I8xCompleteSysButtonIrp$0$anon2_Else#2 - daytona_bug2_ioctl_example_2.bpl(3008,3): inline$I8xKeyboardGetSysButtonEvent$0$anon42_Else#2 - daytona_bug2_ioctl_example_2.bpl(3015,3): inline$I8xKeyboardGetSysButtonEvent$0$label_52#2 - daytona_bug2_ioctl_example_2.bpl(3159,3): inline$I8xKeyboardGetSysButtonEvent$0$label_1#2 - daytona_bug2_ioctl_example_2.bpl(3177,3): inline$I8xDeviceControl$0$anon18_Else#2 - daytona_bug2_ioctl_example_2.bpl(3646,3): inline$I8xDeviceControl$0$label_1#2 - daytona_bug2_ioctl_example_2.bpl(3663,3): inline$dispatch$0$anon5_Else#2 - daytona_bug2_ioctl_example_2.bpl(3694,3): anon31_Then#2 - daytona_bug2_ioctl_example_2.bpl(3699,3): anon17#2 - daytona_bug2_ioctl_example_2.bpl(3756,3): inline$storm_IoCancelIrp$0$anon23_Then#2 - daytona_bug2_ioctl_example_2.bpl(3761,3): inline$storm_IoCancelIrp$0$anon2#2 - daytona_bug2_ioctl_example_2.bpl(3785,3): inline$storm_IoCancelIrp$0$anon25_Then#2 - daytona_bug2_ioctl_example_2.bpl(3790,3): inline$storm_IoCancelIrp$0$anon5#2 - daytona_bug2_ioctl_example_2.bpl(3811,3): inline$storm_IoCancelIrp$0$anon27_Then#2 - daytona_bug2_ioctl_example_2.bpl(3816,3): inline$storm_IoCancelIrp$0$anon8#2 - daytona_bug2_ioctl_example_2.bpl(3827,3): inline$storm_IoCancelIrp$0$anon29_Then#2 - daytona_bug2_ioctl_example_2.bpl(3832,3): inline$storm_IoCancelIrp$0$anon10#2 - daytona_bug2_ioctl_example_2.bpl(3892,3): inline$storm_IoAcquireCancelSpinLock$0$label_11_true#2 - daytona_bug2_ioctl_example_2.bpl(3902,3): inline$storm_IoAcquireCancelSpinLock$0$anon8_Else#2 - daytona_bug2_ioctl_example_2.bpl(3915,3): inline$storm_IoAcquireCancelSpinLock$0$anon9_Else#2 - daytona_bug2_ioctl_example_2.bpl(3923,3): inline$storm_IoAcquireCancelSpinLock$0$anon10_Then#2 - daytona_bug2_ioctl_example_2.bpl(3933,3): inline$storm_IoAcquireCancelSpinLock$0$anon5#2 - daytona_bug2_ioctl_example_2.bpl(3944,3): inline$storm_IoAcquireCancelSpinLock$0$anon11_Then#2 - daytona_bug2_ioctl_example_2.bpl(3949,3): inline$storm_IoAcquireCancelSpinLock$0$anon7#2 - daytona_bug2_ioctl_example_2.bpl(3953,3): inline$storm_IoAcquireCancelSpinLock$0$label_1#2 - daytona_bug2_ioctl_example_2.bpl(3966,3): inline$storm_IoCancelIrp$0$anon30_Else#2 - daytona_bug2_ioctl_example_2.bpl(3984,3): inline$storm_IoCancelIrp$0$label_16_true#2 - daytona_bug2_ioctl_example_2.bpl(4002,3): inline$storm_IoCancelIrp$0$label_22_true#2 - daytona_bug2_ioctl_example_2.bpl(4010,3): inline$storm_IoCancelIrp$0$anon32_Else#2 - daytona_bug2_ioctl_example_2.bpl(4023,3): inline$storm_IoCancelIrp$0$label_27_false#2 - daytona_bug2_ioctl_example_2.bpl(4724,3): inline$storm_IoCancelIrp$0$label_1#2 - daytona_bug2_ioctl_example_2.bpl(4759,3): inline$cancel$0$anon2_Then#2 - daytona_bug2_ioctl_example_2.bpl(4773,3): anon32_Then#2 - daytona_bug2_ioctl_example_2.bpl(4778,3): anon19#2 - daytona_bug2_ioctl_example_2.bpl(4816,3): anon33_Then#2 - daytona_bug2_ioctl_example_2.bpl(4821,3): anon21#2 Boogie program verifier finished with 0 verified, 1 error diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/CorrectTypeConv.bpl boogie-2.4.1+dfsg/Test/roundingmodes/CorrectTypeConv.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/CorrectTypeConv.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/CorrectTypeConv.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,31 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "(_ to_fp 8 24)"} TO_FLOAT32_INT(rmode, int) returns (float24e8); +function {:builtin "(_ to_fp 8 24)"} TO_FLOAT32_REAL(rmode ,real) returns (float24e8); +function {:builtin "(_ to_fp 8 24)"} TO_FLOAT32_BV32(rmode, bv32) returns (float24e8); +function {:builtin "(_ to_fp 11 53)"} TO_FLOAT64_BV64(rmode, bv64) returns (float53e11); +function {:builtin "(_ to_fp 8 24) RTN"} TO_FLOAT32_FLOAT64(float53e11) returns (float24e8); +function {:builtin "(_ to_fp 11 53) RTZ"} TO_FLOAT64_FLOAT32(float24e8) returns (float53e11); + +procedure main() returns () { + var i : int; + var r : real; + var f32 : float24e8; + var f64 : float53e11; + + f32 := TO_FLOAT32_INT(RNA, 5); + f32 := TO_FLOAT32_REAL(RTP, 5.0); + + f32 := TO_FLOAT32_BV32(RNE, 0bv32); + f64 := TO_FLOAT64_BV64(RTN, 0bv64); + + f32 := TO_FLOAT32_FLOAT64(0x0.0e0f53e11); + f64 := TO_FLOAT64_FLOAT32(0x0.0e0f24e8); + + f32 := TO_FLOAT32_FLOAT64(f64); + f64 := TO_FLOAT64_FLOAT32(f32); + + return; +} + diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/CorrectTypeConv.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/CorrectTypeConv.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/CorrectTypeConv.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/CorrectTypeConv.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/DiffModesNotEqual.bpl boogie-2.4.1+dfsg/Test/roundingmodes/DiffModesNotEqual.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/DiffModesNotEqual.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/DiffModesNotEqual.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,13 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure foo() +{ + var a : rmode; + var b : rmode; + + a := RNE; + b := RNA; + + assert (a != b); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/DiffModesNotEqual.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/DiffModesNotEqual.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/DiffModesNotEqual.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/DiffModesNotEqual.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOperators1.bpl boogie-2.4.1+dfsg/Test/roundingmodes/FloatOperators1.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOperators1.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/FloatOperators1.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,17 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "fp.add"} ADD(rmode, float24e8, float24e8) returns (float24e8); + +procedure foo() +{ + var a : float24e8; + var b : float24e8; + var c : float24e8; + var d : float24e8; + + a := 0x1.0e0f24e8; + b := 0x1.0e0f24e8; + c := ADD(RNA, a, b); + d := ADD(RTP, a, b); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOperators1.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/FloatOperators1.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOperators1.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/FloatOperators1.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOperators2.bpl boogie-2.4.1+dfsg/Test/roundingmodes/FloatOperators2.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOperators2.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/FloatOperators2.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,19 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "fp.sqrt"} SQRT(rmode, float24e8) returns (float24e8); + +procedure foo() +{ + var r : rmode; + var a : float24e8; + var b : float24e8; + + a := 0x1.0e0f24e8; + + r := RTN; + b := SQRT(r, a); + + r := RTZ; + b := SQRT(r, a); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOperators2.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/FloatOperators2.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOperators2.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/FloatOperators2.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOpsFixedMode.bpl boogie-2.4.1+dfsg/Test/roundingmodes/FloatOpsFixedMode.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOpsFixedMode.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/FloatOpsFixedMode.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,16 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "fp.mul RNA"} MUL_RNA(float24e8, float24e8) returns (float24e8); + +procedure foo() +{ + var r : rmode; + var a : float24e8; + var b : float24e8; + + a := 0x1.0e0f24e8; + b := 0x1.0e0f24e8; + + b := MUL_RNA(a, b); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOpsFixedMode.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/FloatOpsFixedMode.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOpsFixedMode.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/FloatOpsFixedMode.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOpsWithoutRoundingMode.bpl boogie-2.4.1+dfsg/Test/roundingmodes/FloatOpsWithoutRoundingMode.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FloatOpsWithoutRoundingMode.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/FloatOpsWithoutRoundingMode.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,23 @@ +// RUN: %boogie -proverWarnings:1 "%s" | %OutputCheck "%s" + +function {:builtin "fp.abs RNA"} ABS(float24e8) returns (float24e8); +function {:builtin "fp.leq"} LEQ(rmode, float24e8, float24e8) returns (bool); + +procedure foo() +{ + var r : rmode; + var a : float24e8; + var b : float24e8; + + a := 0x1.0e0f24e8; + b := 0x1.0e0f24e8; + + // CHECK: Prover error: line \d+ column \d+: invalid number of arguments to floating point operator + a := ABS(a); + + if (LEQ(RNA, a, b)) { + a := b; + } +} + +// CHECK-L: Boogie program verifier finished with 0 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FullNameAcronymEquivalence.bpl boogie-2.4.1+dfsg/Test/roundingmodes/FullNameAcronymEquivalence.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FullNameAcronymEquivalence.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/FullNameAcronymEquivalence.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,28 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure foo() +{ + var a : rmode; + var b : rmode; + + a := RNE; + b := roundNearestTiesToEven; + assert (a == b); + + a := RNA; + b := roundNearestTiesToAway; + assert (a == b); + + a := RTP; + b := roundTowardPositive; + assert (a == b); + + a := RTN; + b := roundTowardNegative; + assert (a == b); + + a := RTZ; + b := roundTowardZero; + assert (a == b); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FullNameAcronymEquivalence.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/FullNameAcronymEquivalence.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/FullNameAcronymEquivalence.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/FullNameAcronymEquivalence.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/InvalidFuncName.bpl boogie-2.4.1+dfsg/Test/roundingmodes/InvalidFuncName.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/InvalidFuncName.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/InvalidFuncName.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,4 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "fp.add RNA"} RNA(float24e8, float24e8) returns (float24e8); diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/InvalidFuncName.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/InvalidFuncName.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/InvalidFuncName.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/InvalidFuncName.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ +InvalidFuncName.bpl(4,34): error: ident expected +1 parse errors detected in InvalidFuncName.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/InvalidOperators.bpl boogie-2.4.1+dfsg/Test/roundingmodes/InvalidOperators.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/InvalidOperators.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/InvalidOperators.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,29 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure foo() +{ + var a : rmode; + var b : rmode; + var c : rmode; + + a := RNE; + b := RNE; + + c := a + b; + c := a - b; + c := a * b; + c := a / b; + + if (a <= b) { + c := a; + } else if (a >= b) { + c := b; + } + + if (a < b) { + c := a; + } else if (a > b) { + c := b; + } +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/InvalidOperators.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/InvalidOperators.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/InvalidOperators.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/InvalidOperators.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,13 @@ +InvalidOperators.bpl(13,9): Error: invalid argument types (rmode and rmode) to binary operator + +InvalidOperators.bpl(14,9): Error: invalid argument types (rmode and rmode) to binary operator - +InvalidOperators.bpl(15,9): Error: invalid argument types (rmode and rmode) to binary operator * +InvalidOperators.bpl(16,9): Error: invalid argument types (rmode and rmode) to binary operator / +InvalidOperators.bpl(18,8): Error: invalid argument types (rmode and rmode) to binary operator <= +(0,-1): Error: invalid argument types (rmode and rmode) to binary operator < +InvalidOperators.bpl(20,15): Error: invalid argument types (rmode and rmode) to binary operator >= +(0,-1): Error: invalid argument types (rmode and rmode) to binary operator > +InvalidOperators.bpl(24,8): Error: invalid argument types (rmode and rmode) to binary operator < +(0,-1): Error: invalid argument types (rmode and rmode) to binary operator <= +InvalidOperators.bpl(26,15): Error: invalid argument types (rmode and rmode) to binary operator > +(0,-1): Error: invalid argument types (rmode and rmode) to binary operator >= +12 type checking errors detected in InvalidOperators.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/RMAttributeInvalid.bpl boogie-2.4.1+dfsg/Test/roundingmodes/RMAttributeInvalid.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/RMAttributeInvalid.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/RMAttributeInvalid.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,15 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "fp.add" :rm "RNE"} ADD(float24e8, float24e8) returns (float24e8); + +procedure foo() +{ + var a : float24e8; + var b : float24e8; + + a := 0x1.0e0f24e8; + b := 0x1.0e0f24e8; + b := ADD(a, b); + assert (b == 0x2.0e0f24e8); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/RMAttributeInvalid.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/RMAttributeInvalid.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/RMAttributeInvalid.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/RMAttributeInvalid.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,3 @@ +RMAttributeInvalid.bpl(4,29): error: "}" expected +RMAttributeInvalid.bpl(4,33): error: invalid Function +2 parse errors detected in RMAttributeInvalid.bpl diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/SameModesEqual.bpl boogie-2.4.1+dfsg/Test/roundingmodes/SameModesEqual.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/SameModesEqual.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/SameModesEqual.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,13 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure foo() +{ + var a : rmode; + var b : rmode; + + a := RNE; + b := RNE; + + assert (a == b); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/SameModesEqual.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/SameModesEqual.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/SameModesEqual.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/SameModesEqual.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/UnaffectedOperators.bpl boogie-2.4.1+dfsg/Test/roundingmodes/UnaffectedOperators.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/UnaffectedOperators.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/UnaffectedOperators.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,31 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function {:builtin "fp.add"} ADD(rmode, float24e8, float24e8) returns (float24e8); +function {:builtin "fp.sub"} SUB(rmode, float24e8, float24e8) returns (float24e8); +function {:builtin "fp.mul"} MUL(rmode, float24e8, float24e8) returns (float24e8); +function {:builtin "fp.div"} DIV(rmode, float24e8, float24e8) returns (float24e8); + +procedure foo(a : float24e8, b : float24e8) +requires b != 0x0.0e0f24e8; +{ + var c : float24e8; + var d : float24e8; + + c := a + b; + d := ADD(RNE, a, b); + assert (c == d); + + c := a - b; + d := SUB(RNE, a, b); + assert (c == d); + + c := a * b; + d := MUL(RNE, a, b); + assert (c == d); + + c := a / b; + d := DIV(RNE, a, b); + assert (c == d); + +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/UnaffectedOperators.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/UnaffectedOperators.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/UnaffectedOperators.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/UnaffectedOperators.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/VariableAssign.bpl boogie-2.4.1+dfsg/Test/roundingmodes/VariableAssign.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/VariableAssign.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/VariableAssign.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,13 @@ +// RUN: %boogie -proverWarnings:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure foo() +{ + var a : rmode; + var b : rmode; + + a := RNE; + b := a; + + assert (a == b); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/VariableAssign.bpl.expect boogie-2.4.1+dfsg/Test/roundingmodes/VariableAssign.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/roundingmodes/VariableAssign.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/roundingmodes/VariableAssign.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,2 @@ + +Boogie program verifier finished with 1 verified, 0 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test0/Irreducible.bpl boogie-2.4.1+dfsg/Test/test0/Irreducible.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test0/Irreducible.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test0/Irreducible.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,10 @@ +// RUN: %boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure Irreducible () +{ +A: goto B,C; +B: goto C,D; +C: goto B; +D: return; +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test0/Irreducible.bpl.expect boogie-2.4.1+dfsg/Test/test0/Irreducible.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test0/Irreducible.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test0/Irreducible.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,4 @@ +Irreducible.bpl(4,11): BP5010: Irreducible flow graphs are unsupported. (encountered in implementation Irreducible). +Irreducible.bpl(4,11): Verification inconclusive (Irreducible) + +Boogie program verifier finished with 0 verified, 0 errors, 1 inconclusive diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/CaptureState.bpl boogie-2.4.1+dfsg/Test/test15/CaptureState.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/CaptureState.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test15/CaptureState.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -7,6 +7,7 @@ const unique F: FieldName; procedure P(this: Ref, x: int, y: int) returns (r: int) + requires 118 <= 3 * x && 4 * x < 163 && Heap[this, F] * 5 == -x; // make output deterministic ensures 0 <= r; { var m: int; diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/CaptureState.bpl.expect boogie-2.4.1+dfsg/Test/test15/CaptureState.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/CaptureState.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test15/CaptureState.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,62 +1,62 @@ -CaptureState.bpl(29,1): Error BP5003: A postcondition might not hold on this return path. -CaptureState.bpl(10,3): Related location: This is the postcondition that might not hold. +CaptureState.bpl(30,1): Error BP5003: A postcondition might not hold on this return path. +CaptureState.bpl(11,3): Related location: This is the postcondition that might not hold. Execution trace: - CaptureState.bpl(14,3): anon0 - CaptureState.bpl(18,5): anon4_Then - CaptureState.bpl(26,5): anon3 + CaptureState.bpl(15,3): anon0 + CaptureState.bpl(19,5): anon4_Then + CaptureState.bpl(27,5): anon3 *** MODEL -$mv_state_const -> 3 -%lbl%@293 -> false -%lbl%+112 -> true -%lbl%+114 -> true -%lbl%+118 -> true -%lbl%+148 -> true +$mv_state_const -> 4 +%lbl%@1 -> false +%lbl%+0 -> true +%lbl%+3 -> true +%lbl%+4 -> true +%lbl%+5 -> true F -> T@FieldName!val!0 Heap -> |T@[Ref,FieldName]Int!val!0| m -> **m -m@0 -> (- 2) -m@1 -> (- 1) -m@3 -> (- 1) +m@0 -> (- 8) +m@1 -> (- 7) +m@3 -> (- 7) r -> **r -r@0 -> (- 2) +r@0 -> (- 14) this -> T@Ref!val!0 -x -> 719 +x -> 40 y -> **y -Select_[Ref,FieldName]$int -> { - |T@[Ref,FieldName]Int!val!0| T@Ref!val!0 T@FieldName!val!0 -> (- 2) - else -> (- 2) -} $mv_state -> { - 3 0 -> true - 3 1 -> true - 3 2 -> true - 3 5 -> true + 4 0 -> true + 4 1 -> true + 4 2 -> true + 4 5 -> true else -> true } +Select_[Ref,FieldName]$int -> { + |T@[Ref,FieldName]Int!val!0| T@Ref!val!0 T@FieldName!val!0 -> (- 8) + else -> (- 8) +} tickleBool -> { - true -> true false -> true + true -> true else -> true } *** STATE Heap -> |T@[Ref,FieldName]Int!val!0| + m -> **m + r -> **r this -> T@Ref!val!0 - x -> 719 + x -> 40 y -> **y - r -> **r - m -> **m *** END_STATE *** STATE top *** END_STATE *** STATE then - m -> (- 2) + m -> (- 8) *** END_STATE *** STATE postUpdate0 - m -> (- 1) + m -> (- 7) *** END_STATE *** STATE end - r -> (- 2) m -> 7 + r -> (- 14) *** END_STATE *** END_MODEL diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/IntInModel.bpl.expect boogie-2.4.1+dfsg/Test/test15/IntInModel.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/IntInModel.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test15/IntInModel.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,11 +1,11 @@ *** MODEL -%lbl%@39 -> false -%lbl%+23 -> true -%lbl%+29 -> true +%lbl%@1 -> false +%lbl%+0 -> true +%lbl%+2 -> true i -> 0 tickleBool -> { - true -> true false -> true + true -> true else -> true } *** END_MODEL diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/ModelTest.bpl.expect boogie-2.4.1+dfsg/Test/test15/ModelTest.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/ModelTest.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test15/ModelTest.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,14 +1,14 @@ *** MODEL -%lbl%@147 -> false -%lbl%+64 -> true -%lbl%+84 -> true +%lbl%@1 -> false +%lbl%+0 -> true +%lbl%+3 -> true i@0 -> 1 j@0 -> 2 j@1 -> 3 j@2 -> 4 tickleBool -> { - true -> true false -> true + true -> true else -> true } *** END_MODEL diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/NullInModel.bpl.expect boogie-2.4.1+dfsg/Test/test15/NullInModel.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test15/NullInModel.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test15/NullInModel.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,12 +1,12 @@ *** MODEL -%lbl%@47 -> false -%lbl%+24 -> true -%lbl%+37 -> true +%lbl%@1 -> false +%lbl%+0 -> true +%lbl%+2 -> true null -> T@ref!val!0 s -> T@ref!val!0 tickleBool -> { - true -> true false -> true + true -> true else -> true } *** END_MODEL diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/LambdaExt.bpl boogie-2.4.1+dfsg/Test/test2/LambdaExt.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/LambdaExt.bpl 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test2/LambdaExt.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -1,4 +1,5 @@ // RUN: %boogie -noinfer "%s" > "%t" +// RUN: %boogie -noinfer -freeVarLambdaLifting "%s" >> "%t" // RUN: %diff "%s.expect" "%t" procedure Simplest() { @@ -67,9 +68,6 @@ } procedure FreeVariables() { - var m : [bool,bool,bool]bool; - var k : [bool,bool]bool; - var f : [bool]bool; var g : [bool]bool; @@ -83,6 +81,16 @@ } else { assert f == g; // should fail } +} + +procedure FreeVariables2() { + var k : [bool,bool]bool; + + var f : [bool]bool; + var g : [bool]bool; + + var a : bool; + var b : bool; f := (lambda r: bool :: k[a,b]); g := (lambda s: bool :: k[b,a]); @@ -91,11 +99,21 @@ } else { assert f == g; // should fail } +} + +procedure FreeVariables3() { + var m : [bool,bool,bool]bool; + + var f : [bool]bool; + var g : [bool]bool; + + var a : bool; + var b : bool; f := (lambda r: bool :: m[a,a,b]); g := (lambda s: bool :: m[a,b,b]); if (a == b) { - assert f == g; // should fail because they are different lambda + assert f == g; // should fail for /freeVarLambdaLifting; OK for max-hole lambda lifting } else { assert f == g; // should fail because they are different lambda } @@ -144,3 +162,29 @@ == (lambda y: Box :: $Box($Unbox(y): int)); } + +function F(int,int): int; +const n: [int]bool; +procedure FreeVarOnlyInTrigger() { + // The following once used to crash, because trigger expressions were not + // visited while computing free variables. + assert (forall m: int :: // error + n == (lambda x: int :: (exists y: int :: { F(m,y) } true))); +} + +type TA; +type TB; +function G(TA, TB): int; +procedure MultipleTriggers() { + // The following once used to crash, because max holes for triggers were + // replaced in the wrong order. + assert (forall a: TA, m: [TB]bool :: // error + m == (lambda y: TB :: (exists x: TB :: { G(a, x) } { m[x] } G(a, x) == 10))); +} + +procedure LetBinder() { + var m: [bool]bool; + // The following once used to crash, because let-bound variables were not + // considered local. + m := (lambda b: bool :: (var u := b; u)); +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/LambdaExt.bpl.expect boogie-2.4.1+dfsg/Test/test2/LambdaExt.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/LambdaExt.bpl.expect 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test2/LambdaExt.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -1,56 +1,100 @@ -LambdaExt.bpl(10,3): Error BP5001: This assertion might not hold. +LambdaExt.bpl(11,3): Error BP5001: This assertion might not hold. Execution trace: - LambdaExt.bpl(6,7): anon0 -LambdaExt.bpl(26,3): Error BP5001: This assertion might not hold. + LambdaExt.bpl(7,7): anon0 +LambdaExt.bpl(27,3): Error BP5001: This assertion might not hold. Execution trace: - LambdaExt.bpl(17,5): anon0 -LambdaExt.bpl(30,3): Error BP5001: This assertion might not hold. + LambdaExt.bpl(18,5): anon0 +LambdaExt.bpl(31,3): Error BP5001: This assertion might not hold. Execution trace: - LambdaExt.bpl(17,5): anon0 -LambdaExt.bpl(34,3): Error BP5001: This assertion might not hold. -Execution trace: - LambdaExt.bpl(34,3): anon0 -LambdaExt.bpl(84,5): Error BP5001: This assertion might not hold. -Execution trace: - LambdaExt.bpl(79,5): anon0 - LambdaExt.bpl(84,5): anon9_Else -LambdaExt.bpl(92,5): Error BP5001: This assertion might not hold. -Execution trace: - LambdaExt.bpl(79,5): anon0 - LambdaExt.bpl(84,5): anon9_Else - LambdaExt.bpl(87,5): anon3 - LambdaExt.bpl(92,5): anon10_Else -LambdaExt.bpl(98,5): Error BP5001: This assertion might not hold. -Execution trace: - LambdaExt.bpl(79,5): anon0 - LambdaExt.bpl(82,5): anon9_Then - LambdaExt.bpl(87,5): anon3 - LambdaExt.bpl(90,5): anon10_Then - LambdaExt.bpl(95,5): anon6 - LambdaExt.bpl(98,5): anon11_Then + LambdaExt.bpl(18,5): anon0 +LambdaExt.bpl(35,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(35,3): anon0 +LambdaExt.bpl(82,5): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(77,5): anon0 + LambdaExt.bpl(82,5): anon3_Else +LambdaExt.bpl(100,5): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(95,5): anon0 + LambdaExt.bpl(100,5): anon3_Else +LambdaExt.bpl(118,5): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(113,5): anon0 + LambdaExt.bpl(118,5): anon3_Else +LambdaExt.bpl(130,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(126,5): anon0 +LambdaExt.bpl(137,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(135,5): anon0 +LambdaExt.bpl(139,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(135,5): anon0 +LambdaExt.bpl(141,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(135,5): anon0 +LambdaExt.bpl(152,5): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(147,5): anon0 + LambdaExt.bpl(152,5): anon3_Else +LambdaExt.bpl(171,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(171,3): anon0 +LambdaExt.bpl(181,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(181,3): anon0 + +Boogie program verifier finished with 5 verified, 14 errors +LambdaExt.bpl(11,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(7,7): anon0 +LambdaExt.bpl(27,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(18,5): anon0 +LambdaExt.bpl(31,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(18,5): anon0 +LambdaExt.bpl(35,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(35,3): anon0 +LambdaExt.bpl(82,5): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(77,5): anon0 + LambdaExt.bpl(82,5): anon3_Else LambdaExt.bpl(100,5): Error BP5001: This assertion might not hold. Execution trace: - LambdaExt.bpl(79,5): anon0 - LambdaExt.bpl(84,5): anon9_Else - LambdaExt.bpl(87,5): anon3 - LambdaExt.bpl(92,5): anon10_Else - LambdaExt.bpl(95,5): anon6 - LambdaExt.bpl(100,5): anon11_Else -LambdaExt.bpl(112,3): Error BP5001: This assertion might not hold. -Execution trace: - LambdaExt.bpl(108,5): anon0 -LambdaExt.bpl(119,3): Error BP5001: This assertion might not hold. -Execution trace: - LambdaExt.bpl(117,5): anon0 -LambdaExt.bpl(121,3): Error BP5001: This assertion might not hold. + LambdaExt.bpl(95,5): anon0 + LambdaExt.bpl(100,5): anon3_Else +LambdaExt.bpl(116,5): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(113,5): anon0 + LambdaExt.bpl(116,5): anon3_Then +LambdaExt.bpl(118,5): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(113,5): anon0 + LambdaExt.bpl(118,5): anon3_Else +LambdaExt.bpl(130,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(126,5): anon0 +LambdaExt.bpl(137,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(135,5): anon0 +LambdaExt.bpl(139,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(135,5): anon0 +LambdaExt.bpl(141,3): Error BP5001: This assertion might not hold. +Execution trace: + LambdaExt.bpl(135,5): anon0 +LambdaExt.bpl(152,5): Error BP5001: This assertion might not hold. Execution trace: - LambdaExt.bpl(117,5): anon0 -LambdaExt.bpl(123,3): Error BP5001: This assertion might not hold. + LambdaExt.bpl(147,5): anon0 + LambdaExt.bpl(152,5): anon3_Else +LambdaExt.bpl(171,3): Error BP5001: This assertion might not hold. Execution trace: - LambdaExt.bpl(117,5): anon0 -LambdaExt.bpl(134,5): Error BP5001: This assertion might not hold. + LambdaExt.bpl(171,3): anon0 +LambdaExt.bpl(181,3): Error BP5001: This assertion might not hold. Execution trace: - LambdaExt.bpl(129,5): anon0 - LambdaExt.bpl(134,5): anon3_Else + LambdaExt.bpl(181,3): anon0 -Boogie program verifier finished with 4 verified, 13 errors +Boogie program verifier finished with 5 verified, 15 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/LambdaLifting.bpl boogie-2.4.1+dfsg/Test/test2/LambdaLifting.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/LambdaLifting.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test2/LambdaLifting.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,52 @@ +// RUN: %boogie -noinfer "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure ReducingLambdaBodies() { + var a, b: int; + var f, g: [int]int; + + f := (lambda x: int :: a + b); + g := (lambda x: int :: b + a); + assert f == g; // should pass + + f := (lambda x: int :: x + a); + g := (lambda x: int :: a + x); + assert f == g; // should fail +} + +procedure ReducingLambdaBodies2() { + var a, b: int; + var f, g: [int]int; + var f2, g2: [int,int]int; + + f := (lambda x: int :: x + a); + g := (lambda x: int :: a + x); + assert f != g; // should fail +} + +procedure ReducingLambdaBodies3() { + var a, b: int; + var f, g: [int,int]int; + f := (lambda x, y: int :: x + y); + g := (lambda x, y: int :: y + x); + assert f == g; // should fail +} + +procedure MultibleBoundVars() { + var a, b: int; + var f, g: [int,int,bool]bool; + f := (lambda x: int, y: int, z: bool :: if y == (a - b) then x + (a + b * a) > b else z == (a > b)); + g := (lambda x: int, y: int, z: bool :: if y == (b + a - 2*b) then x + (a * b + a) > b else z == (b < a)); + assert f == g; // should pass +} + +function g(int,int) : int; + +procedure Triggers'(w: int, w': int) { + var a,b : [int]bool; + a := (lambda x:int :: (forall u:int :: { g(u,w) } x == g(u,w))); + b := (lambda y:int :: (forall v:int :: { g(v,w') } y == g(v,w'))); + assert w == w' ==> a == b; + b := (lambda y:int :: (forall v:int :: y == g(v,w'))); + assert w == w' ==> a == b; // should fail because triggers are different +} diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/LambdaLifting.bpl.expect boogie-2.4.1+dfsg/Test/test2/LambdaLifting.bpl.expect --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/LambdaLifting.bpl.expect 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test2/LambdaLifting.bpl.expect 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,14 @@ +LambdaLifting.bpl(14,2): Error BP5001: This assertion might not hold. +Execution trace: + LambdaLifting.bpl(8,4): anon0 +LambdaLifting.bpl(24,2): Error BP5001: This assertion might not hold. +Execution trace: + LambdaLifting.bpl(22,4): anon0 +LambdaLifting.bpl(32,2): Error BP5001: This assertion might not hold. +Execution trace: + LambdaLifting.bpl(30,4): anon0 +LambdaLifting.bpl(51,2): Error BP5001: This assertion might not hold. +Execution trace: + LambdaLifting.bpl(47,4): anon0 + +Boogie program verifier finished with 1 verified, 4 errors diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/Rlimitouts0.bpl boogie-2.4.1+dfsg/Test/test2/Rlimitouts0.bpl --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Test/test2/Rlimitouts0.bpl 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Test/test2/Rlimitouts0.bpl 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,88 @@ +// RUN: %boogie -rlimit:100 "%s" | %OutputCheck "%s" + +procedure TestRlimit0(in: [int]int, len: int) returns (out: [int]int); + requires in[0] == 0 && (forall i: int :: 0 <= i ==> in[i + 1] == in[i] + 1); + requires 0 < len; + ensures (forall j: int :: 0 <= j && j < len ==> out[j] == in[j]); + +implementation TestRlimit0(in: [int]int, len: int) returns (out: [int]int) +{ + // CHECK-L: ${CHECKFILE_NAME}(${LINE:-2},16): Verification out of resource (TestRlimit0) + var i : int; + + i := 0; + out[i] := 0; + while (i < len) + invariant 0 <= i && i <= len; + invariant out[0] == 0 && (forall j: int :: 0 <= j && j < i ==> out[j] == in[j]); + { + out[i] := in[i]; + i := i + 1; + } + + i := 0; + while (i < len) + invariant 0 <= i && i <= len; + invariant (forall j: int :: 0 <= j && j < i ==> out[j] == in[j]); + { + i := i + 1; + } +} + +procedure TestRlimit1(in: [int]int, len: int) returns (out: [int]int); + requires in[0] == 0 && (forall i: int :: 0 <= i ==> in[i + 1] == in[i] + 1); + requires 0 < len; + ensures (forall j: int :: 0 <= j && j < len ==> out[j] == in[j]); + +implementation {:rlimit 60000} TestRlimit1(in: [int]int, len: int) returns (out: [int]int) +{ + var i : int; + + i := 0; + out[i] := 0; + while (i < len) + invariant 0 <= i && i <= len; + invariant out[0] == 0 && (forall j: int :: 0 <= j && j < i ==> out[j] == in[j]); + { + out[i] := in[i]; + i := i + 1; + } + + i := 0; + while (i < len) + invariant 0 <= i && i <= len; + invariant (forall j: int :: 0 <= j && j < i ==> out[j] == in[j]); + { + i := i + 1; + } +} + +procedure TestRlimit2(in: [int]int, len: int) returns (out: [int]int); + requires in[0] == 0 && (forall i: int :: 0 <= i ==> in[i + 1] == in[i] + 1); + requires 0 < len; + ensures (forall j: int :: 0 <= j && j < len ==> out[j] == in[j]); + +implementation {:rlimit 200} TestRlimit2(in: [int]int, len: int) returns (out: [int]int) +{ + // CHECK-L: ${CHECKFILE_NAME}(${LINE:-2},30): Verification out of resource (TestRlimit2) + var i : int; + + i := 0; + out[i] := 0; + while (i < len) + invariant 0 <= i && i <= len; + invariant out[0] == 0 && (forall j: int :: 0 <= j && j < i ==> out[j] == in[j]); + { + out[i] := in[i]; + i := i + 1; + } + + i := 0; + while (i < len) + invariant 0 <= i && i <= len; + invariant (forall j: int :: 0 <= j && j < i ==> out[j] == in[j]); + { + i := i + 1; + } +} +// CHECK-L: Boogie program verifier finished with 1 verified, 0 errors, 2 out of resource diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/.travis.yml boogie-2.4.1+dfsg/.travis.yml --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/.travis.yml 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/.travis.yml 2019-10-25 18:17:05.000000000 +0000 @@ -1,20 +1,22 @@ # vim: set sw=2 ts=2 softtabstop=2 expandtab: language: csharp +dist: trusty sudo: true solution: "Source/Boogie.sln" env: - - BOOGIE_CONFIG=Debug - - BOOGIE_CONFIG=Release + global: + - Z3URL=https://github.com/Z3Prover/z3/releases/download/z3-4.8.4/z3-4.8.4.d6df51951f4c-x64-ubuntu-14.04.zip + matrix: + - BOOGIE_CONFIG=Debug + - BOOGIE_CONFIG=Release install: - wget http://download.opensuse.org/repositories/home:delcypher:z3/xUbuntu_12.04/Release.key - sudo apt-key add - < Release.key - # Use Z3 package built by the OpenSUSE build service https://build.opensuse.org/package/show/home:delcypher:z3/z3 - - sudo sh -c "echo 'deb http://download.opensuse.org/repositories/home:/delcypher:/z3/xUbuntu_12.04/ /' >> /etc/apt/sources.list.d/z3.list" - - sudo apt-get update + # Download a Z3 release + - wget ${Z3URL} + - unzip z3*.zip # NuGet is a little flakey in legacy TravisCI, use travis_retry command to retry the command if it fails - travis_retry nuget restore ${TRAVIS_SOLUTION} - # Install Z3 executable - - sudo apt-get -y install 'z3=4.4.1-*' # Install needed python tools - sudo pip install lit OutputCheck pyyaml - mkdir -p Source/packages && cd Source/packages && travis_retry nuget install NUnit.Runners -Version 2.6.3 @@ -24,5 +26,5 @@ # Run unit tests - python Source/UnitTests/run-unittests.py ${BOOGIE_CONFIG} # Run driver tests - - ln -s /usr/bin/z3 Binaries/z3.exe + - ln -s $(find $PWD/z3* -name z3 -type f) Binaries/z3.exe - lit -v Test/ diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Util/Emacs/boogie-mode.el boogie-2.4.1+dfsg/Util/Emacs/boogie-mode.el --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Util/Emacs/boogie-mode.el 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Util/Emacs/boogie-mode.el 2019-10-25 18:17:05.000000000 +0000 @@ -26,27 +26,42 @@ ; comments have the form /* ... */ '("/\\*\\([^*]\\|\\*[^/]\\)*\\*/" . font-lock-comment-face) ; or // ... - '("//\\([^ -]\\)*" . font-lock-comment-face) - + '("//[^\r\n]*" . font-lock-comment-face) `(,(boogie-regexp-opt '( "type" "const" "function" "axiom" "var" "procedure" "implementation" "returns" "where" "requires" "ensures" "modifies" "free" "unique" "invariant" "extends" "complete" - )) . font-lock-builtin-face) + )) . font-lock-keyword-face) `(,(boogie-regexp-opt '( "assert" "assume" "break" "call" "then" "else" "havoc" "if" "goto" "return" "while" "old" "forall" "exists" "lambda" "cast" "div" "mod" - "false" "true")) . font-lock-keyword-face) - `(,(boogie-regexp-opt '("bool" "int" "real" - "bv0" "bv1" "bv2" "bv3" "bv4" "bv5" "bv6" "bv7" "bv8" "bv9" - "bv10" "bv11" "bv12" "bv13" "bv14" "bv15" "bv16" "bv17" "bv18" "bv19" - "bv20" "bv21" "bv22" "bv23" "bv24" "bv25" "bv26" "bv27" "bv28" "bv29" - "bv30" "bv31" "bv32" "bv33" "bv34" "bv35" "bv36" "bv37" "bv38" "bv39" - "bv40" "bv41" "bv42" "bv43" "bv44" "bv45" "bv46" "bv47" "bv48" "bv49" - "bv50" "bv51" "bv52" "bv53" "bv54" "bv55" "bv56" "bv57" "bv58" "bv59" - "bv60" "bv61" "bv62" "bv63" "bv64" ; and so on - )) . font-lock-type-face) + )) . font-lock-keyword-face) + `(,(boogie-regexp-opt '( + "builtin" "inline" "datatype" "constructor" + )) . font-lock-preprocessor-face) + ;; CIVL stuff + `(,(boogie-regexp-opt '( + "async" "par" + )) . font-lock-keyword-face) + `(,(boogie-regexp-opt '( + "layer" "yields" "yield_assert" + "atomic" "left" "right" "both" + "linear" "linear_in" "linear_out" + "yield" + )) . font-lock-preprocessor-face) + `(,(boogie-regexp-opt '( + "false" "true" + )) . font-lock-constant-face) + `(,(boogie-regexp-opt '( + "bool" "int" "real" + "bv0" "bv1" "bv2" "bv3" "bv4" "bv5" "bv6" "bv7" "bv8" "bv9" + "bv10" "bv11" "bv12" "bv13" "bv14" "bv15" "bv16" "bv17" "bv18" "bv19" + "bv20" "bv21" "bv22" "bv23" "bv24" "bv25" "bv26" "bv27" "bv28" "bv29" + "bv30" "bv31" "bv32" "bv33" "bv34" "bv35" "bv36" "bv37" "bv38" "bv39" + "bv40" "bv41" "bv42" "bv43" "bv44" "bv45" "bv46" "bv47" "bv48" "bv49" + "bv50" "bv51" "bv52" "bv53" "bv54" "bv55" "bv56" "bv57" "bv58" "bv59" + "bv60" "bv61" "bv62" "bv63" "bv64" ; and so on + )) . font-lock-type-face) ) "Minimal highlighting for Boogie mode") @@ -108,6 +123,7 @@ ; indentation ; (make-local-variable 'indent-line-function) ; (setq indent-line-function 'boogie-indent-line) + (setq-local tab-width 2) ; menu ; providing the mode (setq major-mode 'boogie-mode) diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Util/vim/syntax/boogie.vim boogie-2.4.1+dfsg/Util/vim/syntax/boogie.vim --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Util/vim/syntax/boogie.vim 2016-10-30 01:53:09.000000000 +0000 +++ boogie-2.4.1+dfsg/Util/vim/syntax/boogie.vim 2019-10-25 18:17:05.000000000 +0000 @@ -4,6 +4,10 @@ " Last Change: Fri Mar 14 13:47:43 WEST 2008 " Filenames: *.bpl +" Comments: +" Make sure to create a file name .vim/ftdetect/bpl.vim containing this line: +" au BufRead,BufNewFile *.bpl set filetype=boogie + if exists("b:current_syntax") finish endif @@ -61,6 +65,7 @@ syn match bplNumber "\(\<\d\+\.\d*\|\.\d\+\)\([eE][-+]\=\d\+\)\=[fFdD]\=" syn match bplNumber "\<\d\+[eE][-+]\=\d\+[fFdD]\=\>" syn match bplNumber "\<\d\+\([eE][-+]\=\d\+\)\=[fFdD]\>" +syn match bplNumber "\<\d\+bv\d\+\>" " The default highlighting. hi def link bplType Type diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Util/VS2010/Boogie/BoogieLanguageService/Integration/Declarations.cs boogie-2.4.1+dfsg/Util/VS2010/Boogie/BoogieLanguageService/Integration/Declarations.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Util/VS2010/Boogie/BoogieLanguageService/Integration/Declarations.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Util/VS2010/Boogie/BoogieLanguageService/Integration/Declarations.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,56 @@ +/*************************************************************************** + +Copyright (c) Microsoft Corporation. All rights reserved. +This code is licensed under the Visual Studio SDK license terms. +THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. + +***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Package; + +namespace Demo +{ + public class Declarations : Microsoft.VisualStudio.Package.Declarations + { + IList declarations; + public Declarations(IList declarations) + { + this.declarations = declarations; + } + + public override int GetCount() + { + return declarations.Count; + } + + public override string GetDescription(int index) + { + return declarations[index].Description; + } + + public override string GetDisplayText(int index) + { + return declarations[index].DisplayText; + } + + public override int GetGlyph(int index) + { + return declarations[index].Glyph; + } + + public override string GetName(int index) + { + if (index >= 0) + return declarations[index].Name; + + return null; + } + } +} \ No newline at end of file diff -Nru boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Util/VS2010/Boogie/BoogieLanguageService/Integration/Source.cs boogie-2.4.1+dfsg/Util/VS2010/Boogie/BoogieLanguageService/Integration/Source.cs --- boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Util/VS2010/Boogie/BoogieLanguageService/Integration/Source.cs 1970-01-01 00:00:00.000000000 +0000 +++ boogie-2.4.1+dfsg/Util/VS2010/Boogie/BoogieLanguageService/Integration/Source.cs 2019-10-25 18:17:05.000000000 +0000 @@ -0,0 +1,41 @@ +/*************************************************************************** + +Copyright (c) Microsoft Corporation. All rights reserved. +This code is licensed under the Visual Studio SDK license terms. +THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. + +***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Package; + +namespace Demo +{ + public class Source : Microsoft.VisualStudio.Package.Source + { + public Source(LanguageService service, IVsTextLines textLines, Colorizer colorizer) + : base(service, textLines, colorizer) + { + } + + private object parseResult; + public object ParseResult + { + get { return parseResult; } + set { parseResult = value; } + } + + private IList braces; + public IList Braces + { + get { return braces; } + set { braces = value; } + } + } +} \ No newline at end of file Binary files /tmp/tmpqXXgUl/NydRD96wPw/boogie-2.3.0.61016+dfsg+3.gbp1f2d6c1/Util/VS2010/Boogie/BoogieLanguageService/Resources/Irony.dll and /tmp/tmpqXXgUl/ajvIEqV2mG/boogie-2.4.1+dfsg/Util/VS2010/Boogie/BoogieLanguageService/Resources/Irony.dll differ