diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 924d325d815..1354126dbf5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,10 @@ -Fixes # + +### What was changed? - - - - - - - +### How has this been tested? + By submitting this pull request, I confirm that my contribution is made under the terms of the [MIT license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt). diff --git a/.github/workflows/check-deep-tests-reusable.yml b/.github/workflows/check-deep-tests-reusable.yml index c2c6e23dcd2..eb38ecebc98 100644 --- a/.github/workflows/check-deep-tests-reusable.yml +++ b/.github/workflows/check-deep-tests-reusable.yml @@ -1,28 +1,26 @@ name: Check Deep Tests (Reusable Workflow) on: + workflow_dispatch: workflow_call: - inputs: - sha: - type: string - branch: - type: string jobs: check-deep-tests: runs-on: ubuntu-20.04 steps: - name: Checkout Dafny - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: dafny submodules: recursive - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 if: github.repository_owner == 'dafny-lang' with: + # Since nightly-build.yml always targets fixed branches now, rather than being parameterized by + # branch, we can't effectively check "for a specific branch". + # That means we have to be less precise for now and block all branches if any branch fails the deep nightly tests. script: | const script = require('${{ github.workspace }}/dafny/.github/workflows/check-for-workflow-run.js') console.log(script({github, context, core, - workflow_id: 'deep-tests.yml', - ...('${{ inputs.sha }}' ? {sha: '${{ inputs.sha }}'} : {}), - ...('${{ inputs.branch }}' ? {branch: '${{ inputs.branch }}'} : {})})) + workflow_id: 'nightly-build.yml', + branch: 'master'})) diff --git a/.github/workflows/check-for-workflow-run.js b/.github/workflows/check-for-workflow-run.js index e0a2ee1c766..f4b86dd90fb 100644 --- a/.github/workflows/check-for-workflow-run.js +++ b/.github/workflows/check-for-workflow-run.js @@ -14,16 +14,33 @@ module.exports = async ({github, context, core, workflow_id, sha, ...config}) => // These are ordered by creation time, so decide based on the first // run for this SHA we see. const runFilterDesc = sha ? `${workflow_id} on ${sha}` : workflow_id - for (const run of result.data.workflow_runs) { - if ((!sha || run.head_sha === sha) && run.status !== "in_progress") { - if (run.conclusion !== "success") { - core.setFailed(`Last run of ${runFilterDesc} did not succeed: ${run.html_url}`) - } else { - // The SHA is fully tested, exit with success - console.log(`Last run of ${runFilterDesc} succeeded: ${run.html_url}`) - } + const workflow_runs = result.data.workflow_runs.filter(run => !sha || run.head_sha === sha) + const workflow_runs_completed = workflow_runs.filter(run => run.status === "completed") + // The status property can be one of: “queued”, “in_progress”, or “completed”. + const workflow_runs_in_progress = workflow_runs.filter(run => run.status !== "completed") + for (const run of workflow_runs_completed) { + // The conclusion property can be one of: + // “success”, “failure”, “neutral”, “cancelled”, “skipped”, “timed_out”, or “action_required”. + if (run.conclusion === "success") { + // The SHA is fully tested, exit with success + console.log(`Last run of ${runFilterDesc} succeeded: ${run.html_url}`) + return + } else if (run.conclusion === "failure" || run.conclusion === "timed_out") { + const extraMessage = workflow_runs_in_progress.length > 0 ? + `\nA run of ${runFilterDesc} is currently ${workflow_runs_in_progress[0].status}:`+ + ` ${workflow_runs_in_progress[0].html_url}, just re-run this test once it is finished.` : + `\nAt the time of checking, no fix was underway.\n`+ + `- Please first check https://github.com/dafny-lang/dafny/actions/workflows/nightly-build.yml . `+ + `If you see any queued or in progress run on ${runFilterDesc}, just re-run this test once it is finished.`+ + `- If not, and you are a Dafny developer, please fix the issue by creating a PR with the label [run-deep-tests], have it reviewed and merged, ` + + `and then trigger the workflow on ${runFilterDesc} with the URL https://github.com/dafny-lang/dafny/actions/workflows/nightly-build.yml .\n`+ + `With such a label, you can merge a PR even if tests are not successful, but make sure the deeps one are!\n`+ + `If you do not have any clue on how to fix it, `+ + `at worst you can revert all PRs from the last successful run and indicate the authors they need to re-file`+ + ` their PRs and add the label [run-deep-tests] to their PRs`; + core.setFailed(`Last run of ${runFilterDesc} did not succeed: ${run.html_url}${extraMessage}`) return } } - core.setFailed(`No runs of ${runFilterDesc} found!`) + core.setFailed(`No completed runs of ${runFilterDesc} found!`) } diff --git a/.github/workflows/compfuzzci_close_pr.yaml b/.github/workflows/compfuzzci_close_pr.yaml new file mode 100644 index 00000000000..fec95d7b6b2 --- /dev/null +++ b/.github/workflows/compfuzzci_close_pr.yaml @@ -0,0 +1,29 @@ +# This workflow is triggered on PR being closed. +# It dispatches workflow on CompFuzzCI repository, where the bugs found in the PR head is discarded from the database. + +name: Updating CompFuzzCI on PR Closed +on: + pull_request: + branches: + - master + types: [closed] + +jobs: + UpdatePRClosed: + if: github.event.pull_request.base.ref == 'master' && github.event.pull_request.head.repo.owner.login == 'dafny-lang' + runs-on: ubuntu-latest + steps: + - name: Trigger CompFuzzCI + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.COMPFUZZCI_PAT }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'CompFuzzCI', + repo: 'DafnyCompilerFuzzer', + workflow_id: 'update_pr_close.yaml', + ref: 'main', + inputs: { + pr_head_ref: '${{github.event.pull_request.head.ref}}' + } + }) \ No newline at end of file diff --git a/.github/workflows/compfuzzci_fuzz.yaml b/.github/workflows/compfuzzci_fuzz.yaml new file mode 100644 index 00000000000..720f6a02a76 --- /dev/null +++ b/.github/workflows/compfuzzci_fuzz.yaml @@ -0,0 +1,37 @@ +# This workflow is triggered on PR being opened, synced, reopened, closed. +# It dispatches workflow on CompFuzzCI repository, where fuzzing of the PR is handled. +# For problems or suggestions, contact karnbongkot.boonriong23@imperial.ac.uk + +name: Fuzzing on PR +on: + pull_request_target: + branches: + - master + +jobs: + FuzzOnPR: + if: github.event.pull_request.base.ref == 'master' && + (github.event.pull_request.author_association == 'COLLABORATOR' || + github.event.pull_request.author_association == 'MEMBER' || + github.event.pull_request.author_association == 'OWNER') + runs-on: ubuntu-latest + steps: + - name: Trigger CompFuzzCI + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.COMPFUZZCI_PAT }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'CompFuzzCI', + repo: 'DafnyCompilerFuzzer', + workflow_id: 'fuzz.yaml', + ref: 'main', + inputs: { + pr: '${{github.event.pull_request.number}}', + author: '${{github.event.pull_request.user.login}}', + branch: '${{github.event.pull_request.head.ref}}', + head_sha: '${{github.event.pull_request.head.sha}}', + duration: '3600', + instance: '2' + } + }) \ No newline at end of file diff --git a/.github/workflows/compfuzzci_process_issues.yaml b/.github/workflows/compfuzzci_process_issues.yaml new file mode 100644 index 00000000000..2228ef0a2d9 --- /dev/null +++ b/.github/workflows/compfuzzci_process_issues.yaml @@ -0,0 +1,51 @@ +# This workflow is triggered on issue being opened, closed, reopened. +# The CompFuzzCI fuzzer needs to keep track of active issues in the repository to ensure that the fuzzer does not report the same issue multiple times. +# For open and reopen events: It dispatches workflow on CompFuzzCI repository, where the issue is added to the database. +# For close event: It dispatches workflow on CompFuzzCI repository, where the issue is removed from the database. + +name: Issue Update for Fuzzer +on: + issues: + branches: + - master + types: [opened, closed, reopened] + +jobs: + UpdateIssueOpened: + if: github.event.action == 'opened' || github.event.action == 'reopened' + runs-on: ubuntu-latest + steps: + - name: Trigger CompFuzzCI + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.COMPFUZZCI_PAT }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'CompFuzzCI', + repo: 'DafnyCompilerFuzzer', + workflow_id: 'update_issue_open.yaml', + ref: 'main', + inputs: { + issue_number: '${{github.event.issue.number}}', + issuer: '${{github.event.issue.user.login}}', + commit: '${{ github.sha }}' + } + }) + UpdateIssueClosed: + if: github.event.action == 'closed' + runs-on: ubuntu-latest + steps: + - name: Trigger CompFuzzCI + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.COMPFUZZCI_PAT }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'CompFuzzCI', + repo: 'DafnyCompilerFuzzer', + workflow_id: 'update_issue_close.yaml', + ref: 'main', + inputs: { + issue_number: '${{github.event.issue.number}}' + } + }) \ No newline at end of file diff --git a/.github/workflows/daily-soak-test-build.yml b/.github/workflows/daily-soak-test-build.yml new file mode 100644 index 00000000000..98f9025f244 --- /dev/null +++ b/.github/workflows/daily-soak-test-build.yml @@ -0,0 +1,23 @@ + +# Scheduled daily build +# +# The purpose of this build is to run tests that may have non-deterministic failures +# many times over, in the hopes of more aggressively revealing +# flaky tests that occasionally slow down unrelated development. + +name: Daily soak test workflow + +on: + schedule: + # Chosen to be hopefully outside of business hours for most contributors' + # time zones, and not on the hour to avoid heavy scheduled-job times: + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule + - cron: "30 3 * * *" + workflow_dispatch: + +jobs: + daily-soak-build-for-master: + if: github.repository_owner == 'dafny-lang' || github.event_name == 'workflow_dispatch' + uses: ./.github/workflows/xunit-tests-reusable.yml + with: + soak_test: true diff --git a/.github/workflows/deep-tests.yml b/.github/workflows/deep-tests.yml deleted file mode 100644 index 16228a31723..00000000000 --- a/.github/workflows/deep-tests.yml +++ /dev/null @@ -1,58 +0,0 @@ - -# This workflow includes more expensive tests than what is run on every PR, that are unlikely to break on most changes. -# It also publishes a nightly release. -# -# The if at the beginning of each job terminates the workflow immediately on any repo (like a fork) that is not the main -# dafny-lang/dafny repo. This stops such forks from running this workflow and failing (for lack of a secret) the attempt to -# publish a nightly build themselves. - -name: Nightly test and release workflow - -on: - schedule: - # Chosen to be hopefully outside of business hours for most contributors' - # time zones, and not on the hour to avoid heavy scheduled-job times: - # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule - - cron: "30 14 * * *" - workflow_dispatch: - -jobs: - deep-integration-tests: - if: github.repository_owner == 'dafny-lang' - uses: ./.github/workflows/integration-tests-reusable.yml - with: - all_platforms: true - num_shards: 5 - - determine-vars: - if: github.repository_owner == 'dafny-lang' && github.ref == 'refs/heads/master' - runs-on: ubuntu-22.04 - steps: - - name: Checkout Dafny - uses: actions/checkout@v3 - with: - ref: ${{ inputs.sha }} - submodules: recursive - - - name: Get short sha - run: echo "sha_short=`git rev-parse --short HEAD`" >> $GITHUB_ENV - - - name: Get current date - run: echo "date=`date +'%Y-%m-%d'`" >> $GITHUB_ENV - - outputs: - name: nightly-${{ env.date }}-${{ env.sha_short }} - - publish-release: - uses: ./.github/workflows/publish-release-reusable.yml - needs: [deep-integration-tests, determine-vars] - with: - name: ${{ needs.determine-vars.outputs.name }} - sha: ${{ github.sha }} - tag_name: nightly - release_nuget: true - draft: false - release_notes: "This is an automatically published nightly release. This release may not be as stable as versioned releases and does not contain release notes." - prerelease: true - secrets: - nuget_api_key: ${{ secrets.NUGET_API_KEY }} diff --git a/.github/workflows/doc-tests.yml b/.github/workflows/doc-tests.yml index 7680173f8ef..99dc121ca7f 100644 --- a/.github/workflows/doc-tests.yml +++ b/.github/workflows/doc-tests.yml @@ -10,6 +10,7 @@ on: workflow_dispatch: pull_request: branches: [ master, main-* ] + merge_group: concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -18,27 +19,27 @@ concurrency: jobs: check-deep-tests: uses: ./.github/workflows/check-deep-tests-reusable.yml - with: - branch: master doctests: needs: check-deep-tests if: always() && (( github.event_name == 'pull_request' && (needs.check-deep-tests.result == 'success' || contains(github.event.pull_request.labels.*.name, 'run-deep-tests'))) || ( github.event_name == 'push' && ( github.ref_name == 'master' || vars.TEST_ON_FORK == 'true' ))) runs-on: ubuntu-latest - steps: + steps: + # The Windows image was recently updated with a .NET 9 CLI which made CI fail, + # We added a global.json to force it to use any .NET CLI with 8 as a major version - name: Setup dotnet - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x - name: Checkout Dafny - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive path: dafny - name: Load Z3 run: | - sudo apt-get install -qq libarchive-tools + sudo apt-get update && sudo apt-get install -qq libarchive-tools mkdir -p dafny/Binaries/z3/bin wget -qO- https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02/z3-4.12.1-x64-ubuntu-20.04-bin.zip | bsdtar -xf - mv z3-* dafny/Binaries/z3/bin/ diff --git a/.github/workflows/integration-tests-reusable.yml b/.github/workflows/integration-tests-reusable.yml index 905c86531e2..79d72f30dcf 100644 --- a/.github/workflows/integration-tests-reusable.yml +++ b/.github/workflows/integration-tests-reusable.yml @@ -9,10 +9,17 @@ on: num_shards: required: true type: number + ref: + required: true + type: string + compilers: + description: 'Compilers to use' + type: string + required: false + default: "" env: - dotnet-version: 6.0.x # SDK Version for building Dafny - + dotnet-version: 8.0.x # SDK Version for building Dafny jobs: # This job is used to dynamically calculate the matrix dimensions. @@ -24,7 +31,7 @@ jobs: - name: Populate OS list (all platforms) id: populate-os-list-all if: inputs.all_platforms - run: echo "os-list=[\"ubuntu-latest\", \"ubuntu-20.04\", \"macos-latest\", \"windows-2019\"]" >> $GITHUB_OUTPUT + run: echo "os-list=[\"ubuntu-20.04\", \"macos-13\", \"windows-2019\"]" >> $GITHUB_OUTPUT - name: Populate OS list (one platform) id: populate-os-list-one if: "!inputs.all_platforms" @@ -32,7 +39,7 @@ jobs: - name: Populate OS mapping for package.py id: populate-os-mapping run: | - echo "os-mapping={\"ubuntu-latest\": \"ubuntu\", \"ubuntu-20.04\": \"ubuntu\", \"macos-latest\": \"macos\", \"windows-2019\": \"windows\"}" >> $GITHUB_OUTPUT + echo "os-mapping={\"ubuntu-20.04\": \"ubuntu\", \"macos-13\": \"macos\", \"windows-2019\": \"windows\"}" >> $GITHUB_OUTPUT - name: Populate target runtime version list (all platforms) id: populate-target-runtime-version-all if: inputs.all_platforms @@ -50,6 +57,7 @@ jobs: shard-list: ${{ steps.populate-shard-list.outputs.shard-list }} test: needs: populate-matrix-dimensions + timeout-minutes: 120 runs-on: ${{ matrix.os }} strategy: matrix: @@ -61,9 +69,14 @@ jobs: if: runner.os == 'Linux' run: cert-sync /etc/ssl/certs/ca-certificates.crt - name: Setup dotnet - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.dotnet-version }} + # Setup dotnet 6.0 for running Boogie. Alternatively we could try running Boogie with a roll forward policy, or updating Boogie. + - name: Setup dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 6.0.x - name: C++ for ubuntu 20.04 if: matrix.os == 'ubuntu-20.04' run: | @@ -73,31 +86,41 @@ jobs: run: | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 60 - name: Set up oldest supported JDK - if: matrix.target-language-version == 'oldest' - uses: actions/setup-java@v3 + if: matrix.target-language-version != 'newest' + uses: actions/setup-java@v4 with: java-version: 8 distribution: corretto - name: Set up newest supported JDK if: matrix.target-language-version == 'newest' - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 18 distribution: corretto + - name: Set up oldest supported Go + uses: actions/setup-go@v5 + with: + go-version: '1.21' + cache: false + - name: Set up goimports + env: + GO111MODULE: on + run: go install golang.org/x/tools/cmd/goimports@latest - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' - name: Upgrade outdated pip run: python -m pip install --upgrade pip - name: Install lit run: pip install lit OutputCheck pyyaml - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 - run: npm install bignumber.js - name: Checkout Dafny - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: dafny + ref: ${{ inputs.ref }} submodules: true # Until the libraries work again - name: Install Java runtime locally (non-Windows) if: runner.os != 'Windows' @@ -110,6 +133,22 @@ jobs: # - name: Clean up libraries for testing # run: | # rm dafny/Test/libraries/lit.site.cfg # we remove the lit configuration file in the library repo (a git submodule) to prevent override + - name: Use the default Rust linker (Non-Windows) + if: runner.os != 'Windows' + run: rustup default stable + - name: Use specific Toolchain (Windows) + if: runner.os == 'Windows' + run: rustup default stable-x86_64-pc-windows-gnu + - name: Rust-related System information + run: | + echo "What is the Rust version?" + rustc -vV + echo "What is the cargo version?" + cargo --version + echo "What is the architecture?" + uname -m + echo "What are Rust toolchains installed?" + rustup toolchain list - name: Create release if: inputs.all_platforms run: | @@ -124,7 +163,7 @@ jobs: - name: Load Z3 if: "!inputs.all_platforms" run: | - sudo apt-get install -qq libarchive-tools + sudo apt-get update && sudo apt-get install -qq libarchive-tools mkdir -p dafny/Binaries/z3/bin wget -qO- https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02/z3-4.12.1-x64-ubuntu-20.04-bin.zip | bsdtar -xf - mv z3-4.12.1 dafny/Binaries/z3/bin/ @@ -137,9 +176,10 @@ jobs: XUNIT_SHARD: ${{ matrix.shard }} XUNIT_SHARD_COUNT: ${{ inputs.num_shards }} DAFNY_RELEASE: ${{ github.workspace }}\unzippedRelease\dafny + DAFNY_INTEGRATION_TESTS_ONLY_COMPILERS: ${{ inputs.compilers }} run: | cmd /c mklink D:\a\dafny\dafny\unzippedRelease\dafny\z3\bin\z3-4.12.1 D:\a\dafny\dafny\unzippedRelease\dafny\z3\bin\z3-4.12.1.exe - dotnet test --logger trx --logger "console;verbosity=normal" --collect:"XPlat Code Coverage" dafny/Source/IntegrationTests/IntegrationTests.csproj + dotnet test --logger trx --logger "console;verbosity=normal" --collect:"XPlat Code Coverage" --settings dafny/Source/IntegrationTests/coverlet.runsettings dafny/Source/IntegrationTests/IntegrationTests.csproj - name: Generate tests (non-Windows) ## This step creates lit tests from examples in documentation ## These are then picked up by the integration tests below @@ -153,13 +193,14 @@ jobs: env: XUNIT_SHARD: ${{ matrix.shard }} XUNIT_SHARD_COUNT: ${{ inputs.num_shards }} + DAFNY_INTEGRATION_TESTS_ONLY_COMPILERS: ${{ inputs.compilers }} run: | ${{ inputs.all_platforms }} && export DAFNY_RELEASE="${{ github.workspace }}/unzippedRelease/dafny" - dotnet test --logger trx --logger "console;verbosity=normal" --collect:"XPlat Code Coverage" dafny/Source/IntegrationTests - - uses: actions/upload-artifact@v3 + dotnet test --logger trx --logger "console;verbosity=normal" --collect:"XPlat Code Coverage" --settings dafny/Source/IntegrationTests/coverlet.runsettings dafny/Source/IntegrationTests + - uses: actions/upload-artifact@v4 if: always() with: - name: integration-test-results-${{ matrix.os }} + name: integration-test-results-${{ matrix.os }}-${{ matrix.shard }} path: | dafny/Source/*/TestResults/*.trx dafny/Source/*/TestResults/*/coverage.cobertura.xml diff --git a/.github/workflows/jekyll.yml b/.github/workflows/jekyll.yml new file mode 100644 index 00000000000..0727bfc0c73 --- /dev/null +++ b/.github/workflows/jekyll.yml @@ -0,0 +1,60 @@ +# Sample workflow for building and deploying a Jekyll site to GitHub Pages +name: Deploy Jekyll site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.1' # Not needed with a .ruby-version file + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + cache-version: 0 # Increment this number if you need to re-download cached gems + working-directory: 'docs' + - name: Setup Pages + id: pages + uses: actions/configure-pages@v5 + - name: Build with Jekyll + # Outputs to the './_site' directory by default + run: (cd docs; bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" --destination ../_site) + env: + JEKYLL_ENV: production + - name: Upload artifact + # Automatically uploads an artifact from the './_site' directory by default + uses: actions/upload-pages-artifact@v3 + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml index 61805e91a58..5e779c576a8 100644 --- a/.github/workflows/msbuild.yml +++ b/.github/workflows/msbuild.yml @@ -4,6 +4,7 @@ on: workflow_dispatch: pull_request: branches: [ master, main-* ] + merge_group: concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -11,13 +12,11 @@ concurrency: env: - dotnet-version: 6.0.x # SDK Version for building Dafny + dotnet-version: 8.0.x # SDK Version for building Dafny jobs: check-deep-tests: uses: ./.github/workflows/check-deep-tests-reusable.yml - with: - branch: master singletons: needs: check-deep-tests @@ -25,17 +24,23 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Setup dotnet - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{env.dotnet-version}} - name: Checkout Dafny - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: dafny submodules: recursive - name: Restore tools working-directory: dafny run: dotnet tool restore + - name: Check whitespace and style + working-directory: dafny + run: dotnet format whitespace Source/Dafny.sln --verify-no-changes --exclude Source/DafnyCore/Scanner.cs --exclude Source/DafnyCore/Parser.cs --exclude Source/DafnyCore/GeneratedFromDafny/* --exclude Source/DafnyCore.Test/GeneratedFromDafny/* --exclude Source/DafnyRuntime/DafnyRuntimeSystemModule.cs + - name: Check that it's possible to bump the version + working-directory: dafny + run: make bumpversion-test - name: Get Boogie Version run: | sudo apt-get update @@ -45,7 +50,7 @@ jobs: working-directory: dafny run: git apply customBoogie.patch - name: Checkout Boogie - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: boogie-org/boogie path: dafny/boogie @@ -53,11 +58,8 @@ jobs: - name: Build Dafny with local Boogie working-directory: dafny run: dotnet build Source/Dafny.sln - - name: Check whitespace and style - working-directory: dafny - run: dotnet tool run dotnet-format -w -s error --check Source/Dafny.sln --exclude DafnyCore/Scanner.cs --exclude DafnyCore/Parser.cs --exclude DafnyCore/GeneratedFromDafny.cs --exclude DafnyCore/GeneratedFromDafnyRust.cs - name: Create NuGet package (just to make sure it works) - run: dotnet pack --no-build dafny/Source/Dafny.sln + run: dotnet pack dafny/Source/Dafny.sln - name: Check uniformity of integration tests that exercise backends run: DAFNY_INTEGRATION_TESTS_MODE=uniformity-check dotnet test dafny/Source/IntegrationTests @@ -68,14 +70,17 @@ jobs: integration-tests: needs: check-deep-tests - if: always() && (( github.event_name == 'pull_request' && (needs.check-deep-tests.result == 'success' || contains(github.event.pull_request.labels.*.name, 'run-deep-tests'))) || ( github.event_name == 'push' && ( github.ref_name == 'master' || vars.TEST_ON_FORK == 'true' ))) + if: always() && (( github.event_name == 'pull_request' && (needs.check-deep-tests.result == 'success' || contains(github.event.pull_request.labels.*.name, 'run-deep-tests') || contains(github.event.pull_request.labels.*.name, 'run-integration-tests'))) || ( github.event_name == 'push' && ( github.ref_name == 'master' || vars.TEST_ON_FORK == 'true' ))) uses: ./.github/workflows/integration-tests-reusable.yml with: + ref: ${{ github.ref }} # By default run only on one platform, but run on all platforms if the PR has the "run-deep-tests" # label, and skip checking the nightly build above. # This is the best way to fix an issue in master that was only caught by the nightly build. all_platforms: ${{ contains(github.event.pull_request.labels.*.name, 'run-deep-tests') || contains(github.event.push.labels.*.name, 'run-deep-tests')}} num_shards: 5 + # Omit Rust in the nightly build (or rather simulating it here) because Rust is known to have random issues + compilers: ${{ (contains(github.event.pull_request.labels.*.name, 'run-deep-tests') || contains(github.event.push.labels.*.name, 'run-deep-tests')) && 'cs,java,go,js,cpp,dfy,py' || '' }} test-coverage-analysis: runs-on: ubuntu-20.04 @@ -86,13 +91,13 @@ jobs: steps: # Check out Dafny so that highlighted source is possible - name: Checkout Dafny - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: dafny submodules: recursive - name: Setup dotnet - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{env.dotnet-version}} @@ -109,12 +114,13 @@ jobs: dotnet tool run coco "Source/DafnyCore/Dafny.atg" -namespace Microsoft.Dafny -frames "Source/DafnyCore/Coco" - name: Download integration test artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: integration-test-results-ubuntu-20.04 + pattern: integration-test-results-ubuntu-20.04-* + merge-multiple: true - name: Download unit test artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: unit-test-results-ubuntu-20.04 @@ -127,7 +133,7 @@ jobs: reportgenerator \ -reports:"./**/coverage.cobertura.xml" \ -reporttypes:Cobertura -targetdir:coverage-cobertura \ - -classfilters:"-Microsoft.Dafny.*PreType*;-Microsoft.Dafny.ResolverPass;-Microsoft.Dafny.*Underspecification*;-Microsoft.Dafny.DetectUnderspecificationVisitor;-Microsoft.Dafny.Microsoft.Dafny.UnderspecificationDetector;-DAST.*;-DCOMP.*" + -classfilters:"-Microsoft.Dafny.*PreType*;-Microsoft.Dafny.ResolverPass;-Microsoft.Dafny.*Underspecification*;-Microsoft.Dafny.DetectUnderspecificationVisitor;-Microsoft.Dafny.Microsoft.Dafny.UnderspecificationDetector;-Microsoft.Dafny.Compilers.DafnyCompiler;-DAST.*;-DCOMP.*" # Generate HTML from combined report, leaving out XUnitExtensions reportgenerator \ -reports:"coverage-cobertura/Cobertura.xml" \ @@ -158,7 +164,7 @@ jobs: output: both # Fail if less than 86% total coverage, measured across all packages combined. # Report "yellow" status if less than 90% total coverage. - thresholds: '86 90' + thresholds: '85 90' - name: Code coverage report (LSP) uses: irongut/CodeCoverageSummary@v1.3.0 @@ -175,7 +181,7 @@ jobs: # Report "yellow" status if less than 90% total coverage. thresholds: '86 90' - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() # Upload results even if the job fails with: name: test-coverage-results diff --git a/.github/workflows/nightly-build-manual.yml b/.github/workflows/nightly-build-manual.yml new file mode 100644 index 00000000000..51b85eafed4 --- /dev/null +++ b/.github/workflows/nightly-build-manual.yml @@ -0,0 +1,26 @@ + +# Manual trigger for the nightly build on a given branch. + +name: Nightly test and release workflow (Manual trigger) + +on: + workflow_dispatch: + inputs: + ref: + description: 'The git ref to run on' + required: true + type: string + publish-prerelease: + description: 'Whether to publish a prerelease' + required: false + type: boolean + default: false + +jobs: + nightly-build: + uses: ./.github/workflows/nightly-build-reusable.yml + with: + ref: ${{ inputs.ref }} + publish-prerelease: ${{ inputs.publish-prerelease }} + secrets: + nuget_api_key: ${{ secrets.NUGET_API_KEY }} diff --git a/.github/workflows/nightly-build-reusable.yml b/.github/workflows/nightly-build-reusable.yml new file mode 100644 index 00000000000..4b659ebba7b --- /dev/null +++ b/.github/workflows/nightly-build-reusable.yml @@ -0,0 +1,63 @@ +# This workflow includes more expensive tests than what is run on every PR, that are unlikely to break on most changes. +# It also optionally publishes a nightly prerelease. + +name: Nightly test and release workflow (Reusable Workflow) + +on: + workflow_call: + inputs: + ref: + required: true + type: string + publish-prerelease: + required: false + type: boolean + default: false + secrets: + # Required if publish-prerelease is true + nuget_api_key: + required: false + +jobs: + deep-integration-tests: + if: github.repository_owner == 'dafny-lang' + uses: ./.github/workflows/integration-tests-reusable.yml + with: + ref: ${{ inputs.ref }} + all_platforms: true + num_shards: 10 + # Omit Rust because Rust is known to have random issues + compilers: cs,java,go,js,cpp,dfy,py + + determine-vars: + if: github.repository_owner == 'dafny-lang' && inputs.publish-prerelease + runs-on: ubuntu-22.04 + steps: + - name: Checkout Dafny + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + submodules: recursive + + - name: Get short sha + run: echo "sha_short=`git rev-parse --short HEAD`" >> $GITHUB_ENV + + - name: Get current date + run: echo "date=`date +'%Y-%m-%d'`" >> $GITHUB_ENV + + outputs: + name: nightly-${{ env.date }}-${{ env.sha_short }} + + publish-release: + uses: ./.github/workflows/publish-release-reusable.yml + needs: [deep-integration-tests, determine-vars] + with: + name: ${{ needs.determine-vars.outputs.name }} + ref: ${{ inputs.ref }} + tag_name: nightly + release_nuget: true + draft: false + release_notes: "This is an automatically published nightly release. This release may not be as stable as versioned releases and does not contain release notes." + prerelease: true + secrets: + nuget_api_key: ${{ secrets.nuget_api_key }} diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml new file mode 100644 index 00000000000..311a0656713 --- /dev/null +++ b/.github/workflows/nightly-build.yml @@ -0,0 +1,33 @@ + +# Scheduled nightly build +# +# Scheduled workflows only ever trigger for the default branch, +# but we need to run nightly jobs on multiple branches. +# This workflow therefore triggers a reusable workflow +# on every branch that needs to be protected with the nightly deep-test mechanism, +# and ensures which ever branch contains the next planned release +# publishes the nightly prerelease. +# +# The if at the beginning of each job terminates the workflow immediately on any repo (like a fork) that is not the main +# dafny-lang/dafny repo. This stops such forks from running this workflow and failing (for lack of a secret) the attempt to +# publish a nightly build themselves. + +name: Nightly test and release workflow + +on: + schedule: + # Chosen to be hopefully outside of business hours for most contributors' + # time zones, and not on the hour to avoid heavy scheduled-job times: + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule + - cron: "30 14 * * *" + workflow_dispatch: + +jobs: + nightly-build-for-master: + if: github.repository_owner == 'dafny-lang' + uses: ./.github/workflows/nightly-build-reusable.yml + with: + ref: master + publish-prerelease: true + secrets: + nuget_api_key: ${{ secrets.NUGET_API_KEY }} diff --git a/.github/workflows/publish-release-reusable.yml b/.github/workflows/publish-release-reusable.yml index 8e6333175bb..adee22747dc 100644 --- a/.github/workflows/publish-release-reusable.yml +++ b/.github/workflows/publish-release-reusable.yml @@ -9,7 +9,7 @@ on: prerelease: required: false type: boolean - sha: + ref: required: true type: string draft: @@ -30,20 +30,20 @@ on: required: false env: - dotnet-version: 6.0.x # SDK Version for building Dafny + dotnet-version: 8.0.x # SDK Version for building Dafny jobs: publish-release: - runs-on: macos-latest + runs-on: macos-13 # Put back 'ubuntu-20.04' if macos-latest fails in any way steps: - name: Print version run: echo ${{ inputs.name }} - name: Checkout Dafny - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: dafny - ref: ${{ inputs.sha }} + ref: ${{ inputs.ref }} - name: Ensure tag exists if: ${{ inputs.prerelease }} run: | @@ -51,7 +51,7 @@ jobs: git push origin ${{ inputs.tag_name }} -f working-directory: dafny - name: Setup dotnet - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{env.dotnet-version}} - name: C++ for ubuntu 20.04 @@ -63,14 +63,14 @@ jobs: run: | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 60 - name: Set up JDK 18 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 18 distribution: corretto - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 - run: npm install bignumber.js - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' - name: Build Dafny @@ -79,14 +79,14 @@ jobs: rm dafny/Binaries/*.nupkg - name: Create release NuGet package (for uploading) if: ${{ !inputs.prerelease }} - run: dotnet pack --no-build dafny/Source/Dafny.sln + run: dotnet pack dafny/Source/Dafny.sln - name: Create prerelease NuGet package (for uploading) if: ${{ inputs.prerelease }} # NuGet will consider any package with a version-suffix as a prerelease - run: dotnet pack --version-suffix ${{ inputs.name }} --no-build dafny/Source/Dafny.sln + run: dotnet pack --version-suffix ${{ inputs.name }} dafny/Source/Dafny.sln - name: Make NuGet package available as an artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: nuget-packages path: dafny/Binaries/*.nupkg @@ -96,22 +96,31 @@ jobs: # Need --skip-duplicate for cases where we release a new Dafny # version but the runtime hasn't changed and is still the old # version. - run: dotnet nuget push --skip-duplicate "dafny/Binaries/Dafny*.nupkg" -k ${{ secrets.nuget_api_key }} -s https://api.nuget.org/v3/index.json + run: dotnet nuget push --skip-duplicate "dafny/Binaries/Dafny*.nupkg" -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json - - name: Install latex pandoc + - name: Install pandoc - Linux + if: runner.os == 'Linux' + run: | + wget https://github.com/jgm/pandoc/releases/download/3.1.7/pandoc-3.1.7-1-amd64.deb + sudo dpkg -i *.deb + rm -rf *.deb + pandoc -v + sudo gem install rouge + - name: Install pandoc - MacOS + if: runner.os == 'MacOS' run: | unset HOMEBREW_NO_INSTALL_FROM_API brew untap Homebrew/core brew update-reset && brew update brew install pandoc - brew install --cask mactex - eval "$(/usr/libexec/path_helper)" - sudo tlmgr update --self - sudo tlmgr install framed tcolorbox environ trimspaces unicode-math pandoc -v - which latex || echo NOT FOUND latex - which xelatex || echo NOT FOUND xelatex sudo gem install rouge -v 3.30.0 + - name: Install Tectonic + uses: wtfjoke/setup-tectonic@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + tectonic-version: 0.15 + - name: Clean Dafny run: dotnet clean dafny/Source/Dafny.sln # First we build the ZIPs (which do not include the refman) @@ -128,14 +137,17 @@ jobs: # Additionally, since the refman build scripts expect to find Dafny in its usual Binaries/ folder (not in # a platform-specific directory), we build Dafny once here. - name: Re-build Dafny + if: ${{ !inputs.prerelease }} run: dotnet build dafny/Source/Dafny.sln - name: Build reference manual + if: ${{ !inputs.prerelease }} run: | eval "$(/usr/libexec/path_helper)" make -C dafny/docs/DafnyRef - - name: Create GitHub release - uses: softprops/action-gh-release@v1 + - name: Create GitHub release (release) + if: ${{ !inputs.prerelease }} + uses: softprops/action-gh-release@v2 with: name: Dafny ${{ inputs.name }} tag_name: ${{ inputs.tag_name }} @@ -146,3 +158,17 @@ jobs: dafny/Package/dafny-${{ inputs.name }}* dafny/docs/DafnyRef/DafnyRef.pdf fail_on_unmatched_files: true + + # This step is separate from the release one because the refman is omitted + - name: Create GitHub release (prerelease) + if: ${{ inputs.prerelease }} + uses: softprops/action-gh-release@v2 + with: + name: Dafny ${{ inputs.name }} + tag_name: ${{ inputs.tag_name }} + body: ${{ inputs.release_notes }} + draft: ${{ inputs.draft }} + prerelease: ${{ inputs.prerelease }} + files: | + dafny/Package/dafny-${{ inputs.name }}* + fail_on_unmatched_files: true diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index fd6476f5188..7b3ee73a7ef 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -7,13 +7,16 @@ on: - 'v*' env: - dotnet-version: 6.0.x # SDK Version for building Dafny + dotnet-version: 8.0.x # SDK Version for building Dafny jobs: - check-deep-tests: - uses: ./.github/workflows/check-deep-tests-reusable.yml + deep-integration-tests: + if: github.repository_owner == 'dafny-lang' + uses: ./.github/workflows/integration-tests-reusable.yml with: - sha: ${{ github.sha }} + ref: ${{ github.ref }} + all_platforms: true + num_shards: 5 get-version: runs-on: ubuntu-22.04 @@ -26,11 +29,11 @@ jobs: version: ${{ steps.get-version.outputs.version-without-v }} publish-release: - needs: [check-deep-tests, get-version] + needs: [deep-integration-tests, get-version] uses: ./.github/workflows/publish-release-reusable.yml with: name: ${{ needs.get-version.outputs.version }} - sha: ${{ github.sha }} + ref: ${{ github.sha }} tag_name: ${{ github.ref }} draft: true release_nuget: true diff --git a/.github/workflows/refman.yml b/.github/workflows/refman.yml index 3a9db4edddc..93a65d4470b 100644 --- a/.github/workflows/refman.yml +++ b/.github/workflows/refman.yml @@ -4,6 +4,7 @@ on: workflow_dispatch: pull_request: branches: [ master, main-* ] + merge_group: concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -12,76 +13,51 @@ concurrency: jobs: check-deep-tests: uses: ./.github/workflows/check-deep-tests-reusable.yml - with: - branch: master - build: + build-refman: needs: check-deep-tests if: always() && (( github.event_name == 'pull_request' && (needs.check-deep-tests.result == 'success' || contains(github.event.pull_request.labels.*.name, 'run-deep-tests'))) || ( github.event_name == 'push' && ( github.ref_name == 'master' || vars.TEST_ON_FORK == 'true' ))) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ macos-latest ] - # os: [macos-latest, ubuntu-latest ] + os: [ ubuntu-22.04 ] steps: - name: OS run: echo ${{ runner.os }} ${{ matrix.os }} - name: Setup dotnet - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x - name: Checkout Dafny - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive path: dafny - name: Build Dafny run: dotnet build dafny/Source/Dafny.sln - - name: Install latex pandoc - Linux + - name: Install pandoc - Linux if: runner.os == 'Linux' run: | - sudo apt-get install texlive texlive-xetex - wget https://github.com/jgm/pandoc/releases/download/2.10.1/pandoc-2.10.1-1-amd64.deb + wget https://github.com/jgm/pandoc/releases/download/3.1.7/pandoc-3.1.7-1-amd64.deb sudo dpkg -i *.deb rm -rf *.deb - # apt-get has pandoc, but it is outdated - - name: Extra linux packages - if: matrix.os == 'ubuntu-latest' - run: | - sudo apt-get install texlive-science - sudo tlmgr init-usertree - sudo tlmgr update --self - sudo tlmgr install framed tcolorbox environ trimspaces unicode-math - pandoc -v - which latex || echo NOT FOUND latex - which xelatex || echo NOT FOUND xelatex - sudo gem install rouge - - if: matrix.os != 'ubuntu-latest' && runner.os == 'Linux' - run: | - sudo apt-get install texlive-math-extra - sudo tlmgr init-usertree - sudo tlmgr update --self - sudo tlmgr install framed tcolorbox environ trimspaces unicode-math pandoc -v - which latex || echo NOT FOUND latex - which xelatex || echo NOT FOUND xelatex sudo gem install rouge - - name: Install latex pandoc - MacOS + - name: Install pandoc - MacOS if: runner.os == 'MacOS' run: | unset HOMEBREW_NO_INSTALL_FROM_API brew untap Homebrew/core brew update-reset && brew update brew install pandoc - brew install --cask basictex - eval "$(/usr/libexec/path_helper)" - sudo tlmgr update --self - sudo tlmgr install framed tcolorbox environ trimspaces unicode-math pandoc -v - which latex || echo NOT FOUND latex - which xelatex || echo NOT FOUND xelatex sudo gem install rouge -v 3.30.0 + - name: Install Tectonic + uses: wtfjoke/setup-tectonic@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + tectonic-version: 0.15 - name: Build reference manual run: | eval "$(/usr/libexec/path_helper)" @@ -89,7 +65,7 @@ jobs: make -C dafny/docs/DafnyRef - name: Check run: ls -la dafny/docs/DafnyRef/DafnyRef.pdf - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: DafnyRef diff --git a/.github/workflows/release-brew.yml b/.github/workflows/release-brew.yml index 70bdfb5de88..c6867f03326 100644 --- a/.github/workflows/release-brew.yml +++ b/.github/workflows/release-brew.yml @@ -10,7 +10,7 @@ concurrency: jobs: build: - runs-on: macos-latest + runs-on: macos-13 steps: - name: Install dafny @@ -26,7 +26,7 @@ jobs: java -version python --version - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' ## To be consistent, the download should be tested with the quicktest.sh corresponding to diff --git a/.github/workflows/release-downloads-nuget.yml b/.github/workflows/release-downloads-nuget.yml index 60c0393ed77..1d08a00ba40 100644 --- a/.github/workflows/release-downloads-nuget.yml +++ b/.github/workflows/release-downloads-nuget.yml @@ -17,7 +17,7 @@ on: workflow_dispatch: env: - dotnet-version: 6.0.x # SDK Version for running Dafny (TODO: should this be an older version?) + dotnet-version: 8.0.x # SDK Version for running Dafny (TODO: should this be an older version?) z3BaseUri: https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02 concurrency: @@ -32,18 +32,18 @@ jobs: fail-fast: false matrix: # This workflow breaks on windows-2022: https://github.com/dafny-lang/dafny/issues/1906 - os: [ ubuntu-22.04, ubuntu-20.04, macos-11, windows-2019 ] + os: [ ubuntu-22.04, ubuntu-20.04, macos-13, windows-2019 ] steps: - name: OS run: echo ${{ runner.os }} ${{ matrix.os }} - name: Set up JDK 18 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 18 distribution: corretto - name: Setup dotnet - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{env.dotnet-version}} - name: Load Z3 @@ -85,13 +85,13 @@ jobs: excludes: prerelease, draft - name: Verify Dafny version if: "${{ steps.dafny.outputs.release != '' }}" - run: version="${{ steps.dafny.outputs.release }}"; dafny -version | grep -iE "Dafny "${version:1}".[0-9]{5}" + run: version="${{ steps.dafny.outputs.release }}"; dafny -version | grep -iE "Dafny "${version:1}".[0-9]+" shell: bash ## Check that a simple program compiles and runs on each supported platform ## Now that the dotnet tool distribution doesn't include the Scripts, ## so we need to clone the repository to get them. - name: Checkout Dafny - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive path: dafny-repo @@ -111,7 +111,7 @@ jobs: # but note we need to skip Dafny since nuget install doesn't work for dotnet tools. library-name: [ DafnyPipeline, DafnyServer, DafnyLanguageServer, DafnyRuntime, DafnyCore, DafnyDriver ] # This workflow breaks on windows-2022: https://github.com/dafny-lang/dafny/issues/1906 - os: [ ubuntu-latest, ubuntu-20.04, macos-latest, windows-2019 ] + os: [ ubuntu-latest, ubuntu-20.04, macos-13, windows-2019 ] steps: # Verify that the dependencies of the libraries we publish (e.g. DafnyLanguageServer) diff --git a/.github/workflows/release-downloads.yml b/.github/workflows/release-downloads.yml index 8cac8cd3ac8..74c68178672 100644 --- a/.github/workflows/release-downloads.yml +++ b/.github/workflows/release-downloads.yml @@ -18,14 +18,14 @@ jobs: fail-fast: false matrix: # This workflow breaks on windows-2022: https://github.com/dafny-lang/dafny/issues/1906 - os: [ ubuntu-latest, ubuntu-20.04, macos-latest, windows-2019 ] + os: [ ubuntu-latest, ubuntu-20.04, macos-13, windows-2019 ] include: - os: 'ubuntu-latest' osn: 'ubuntu-20.04' - os: 'ubuntu-20.04' osn: 'ubuntu-20.04' - - os: 'macos-latest' - osn: 'x64-macos-11' + - os: 'macos-13' + osn: 'x64-macos-13' - os: 'windows-2019' osn: 'windows-2019' # There is no hosted environment for Ubuntu 14.04 or for debian @@ -36,7 +36,7 @@ jobs: - name: OS run: echo ${{ runner.os }} ${{ matrix.os }} - name: Set up JDK 18 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 18 distribution: corretto diff --git a/.github/workflows/runtime-tests.yml b/.github/workflows/runtime-tests.yml index ad927af4867..0d5888a133d 100644 --- a/.github/workflows/runtime-tests.yml +++ b/.github/workflows/runtime-tests.yml @@ -4,7 +4,8 @@ on: workflow_dispatch: pull_request: branches: [ master, main-* ] - + merge_group: + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -12,8 +13,6 @@ concurrency: jobs: check-deep-tests: uses: ./.github/workflows/check-deep-tests-reusable.yml - with: - branch: master build: needs: check-deep-tests @@ -23,14 +22,29 @@ jobs: steps: - name: Checkout Dafny - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true + # The Windows image was recently updated with a .NET 9 CLI which made CI fail, + # We added a global.json to force it to use any .NET CLI with 8 as a major version + - name: Setup dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x - name: Set up JDK 18 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 18 distribution: corretto + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.21' + cache: false + - name: Set up goimports + env: + GO111MODULE: on + run: go install golang.org/x/tools/cmd/goimports@latest - name: Build Dafny run: dotnet build Source/Dafny.sln - name: Get Z3 @@ -40,18 +54,36 @@ jobs: cd ./Source/DafnyCore make test make check-format + make check-extract + - name: Test DafnyRuntime (C#, Rust) + run: | + cd ./Source/DafnyRuntime + make all - name: Test DafnyRuntimeDafny run: | cd ./Source/DafnyRuntime/DafnyRuntimeDafny - make test - make check-format + make all - name: Test DafnyRuntimeGo run: | cd ./Source/DafnyRuntime/DafnyRuntimeGo - make test - # This isn't strictly necessary since the Java runtime has to be built into a jar, - # which also runs the tests. - # But I prefer the simplicity of testing every testable runtime in this workflow + make all + - name: Test DafnyRuntimeGo-gomod + run: | + cd ./Source/DafnyRuntime/DafnyRuntimeGo-gomod + make all - name: Test DafnyRuntimeJava - run: ./Source/DafnyRuntime/DafnyRuntimeJava/gradlew -p ./Source/DafnyRuntime/DafnyRuntimeJava test - + run: | + cd ./Source/DafnyRuntime/DafnyRuntimeJava + make all + - name: Test DafnyRuntimePython + run: | + cd ./Source/DafnyRuntime/DafnyRuntimePython + make all + - name: Test DafnyRuntimeJs + run: | + cd ./Source/DafnyRuntime/DafnyRuntimeJs + make all + - name: Test DafnyRuntimeRust + run: | + cd ./Source/DafnyRuntime/DafnyRuntimeRust + make all diff --git a/.github/workflows/standard-libraries.yml b/.github/workflows/standard-libraries.yml new file mode 100644 index 00000000000..dd966bbc82f --- /dev/null +++ b/.github/workflows/standard-libraries.yml @@ -0,0 +1,48 @@ +name: Build and Test Dafny Standard Libraries + +on: + workflow_dispatch: + pull_request: + branches: [ master, main-* ] + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check-deep-tests: + uses: ./.github/workflows/check-deep-tests-reusable.yml + + build: + needs: check-deep-tests + if: always() && (( github.event_name == 'pull_request' && (needs.check-deep-tests.result == 'success' || contains(github.event.pull_request.labels.*.name, 'run-deep-tests'))) || ( github.event_name == 'push' && ( github.ref_name == 'master' || vars.TEST_ON_FORK == 'true' ))) + runs-on: macos-13 + + steps: + - name: Checkout Dafny + uses: actions/checkout@v4 + with: + submodules: true + - name: Set up JDK 18 + uses: actions/setup-java@v4 + with: + java-version: 18 + distribution: corretto + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.21' + cache: false + - name: Set up goimports + env: + GO111MODULE: on + run: go install golang.org/x/tools/cmd/goimports@latest + - name: Build Dafny + run: dotnet build Source/Dafny.sln + - name: Get Z3 + run: make z3-mac + - run: npm install bignumber.js + - name: Test DafnyStandardLibraries + run: make -C Source/DafnyStandardLibraries all + diff --git a/.github/workflows/test-report.yml b/.github/workflows/test-report.yml index 29508c1a211..167889f4b90 100644 --- a/.github/workflows/test-report.yml +++ b/.github/workflows/test-report.yml @@ -14,7 +14,7 @@ jobs: if: always() steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive diff --git a/.github/workflows/xunit-tests-reusable.yml b/.github/workflows/xunit-tests-reusable.yml index dff2b0da5a7..d6b768e6f3a 100644 --- a/.github/workflows/xunit-tests-reusable.yml +++ b/.github/workflows/xunit-tests-reusable.yml @@ -2,7 +2,17 @@ name: Run XUnit tests on: workflow_dispatch: + inputs: + soak_test: + required: false + type: boolean + default: false workflow_call: + inputs: + soak_test: + required: false + type: boolean + default: false ## In the matrix: ## os - name of the Github actions runner @@ -15,15 +25,34 @@ defaults: working-directory: dafny jobs: + populate-matrix-dimensions: + runs-on: ubuntu-latest + steps: + - name: Populate iterations (normal mode) + id: populate-iterations-normal + if: "!inputs.soak_test" + working-directory: . + run: echo "iterations=[1]" >> $GITHUB_OUTPUT + - name: Populate iterations (soak test mode) + id: populate-iterations-soak + if: inputs.soak_test + working-directory: . + run: echo "iterations=[`seq -s , 1 5`]" >> $GITHUB_OUTPUT + outputs: + iterations: ${{ steps.populate-iterations-normal.outputs.iterations }} ${{ steps.populate-iterations-soak.outputs.iterations }} + build: + needs: populate-matrix-dimensions runs-on: ${{matrix.os}} timeout-minutes: 60 - name: ${{matrix.suffix}} + name: ${{matrix.suffix}} (${{matrix.iteration}}) strategy: fail-fast: false matrix: + os: [ubuntu-20.04, windows-2019, macos-13] + iteration: ${{ fromJson(needs.populate-matrix-dimensions.outputs.iterations) }} include: - - os: macos-11 + - os: macos-13 suffix: osx chmod: true - os: windows-2019 @@ -34,17 +63,17 @@ jobs: chmod: true env: solutionPath: Source/Dafny.sln - z3BaseUri: https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02 + z3BaseUri: https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2024-04-10 Logging__LogLevel__Microsoft: Debug steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: dafny submodules: recursive - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x - name: Install dependencies run: | dotnet restore ${{env.solutionPath}} @@ -66,27 +95,34 @@ jobs: - name: Build run: dotnet build --no-restore ${{env.solutionPath}} - name: Run DafnyCore Tests - run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" Source/DafnyCore.Test + if: "!inputs.soak_test" + run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyCore.Test/coverlet.runsettings Source/DafnyCore.Test + - name: Run DafnyLanguageServer Tests (soak test - iteration ${{matrix.iteration}}) + if: inputs.soak_test + run: | + dotnet test --no-restore --blame-crash --blame-hang-timeout 360s --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyLanguageServer.Test/coverlet.runsettings Source/DafnyLanguageServer.Test - name: Run DafnyLanguageServer Tests + if: "!inputs.soak_test" run: | - ## Run twice to catch unstable code (Issue #2673) - dotnet test --no-restore --blame-hang-timeout 360s --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" Source/DafnyLanguageServer.Test - ## On the second run, collect test coverage data ## Note that, for some mysterious reason, --collect doesn't work with the DafnyLanguageServer.Test package dotnet coverage collect -o DafnyLanguageServer.Test.coverage dotnet test --no-restore --blame-hang-timeout 360s --logger "console;verbosity=normal" --logger trx Source/DafnyLanguageServer.Test - - name: Run DafnyDriver Tests - run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" Source/DafnyDriver.Test + if: "!inputs.soak_test" + run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyDriver.Test/coverlet.runsettings Source/DafnyDriver.Test - name: Run DafnyPipeline Tests - run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" Source/DafnyPipeline.Test + if: "!inputs.soak_test" + run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyPipeline.Test/coverlet.runsettings Source/DafnyPipeline.Test - name: Run DafnyTestGeneration Tests - run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" Source/DafnyTestGeneration.Test + if: "!inputs.soak_test" + run: dotnet test --no-restore --blame-hang-timeout 5m --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyTestGeneration.Test/coverlet.runsettings Source/DafnyTestGeneration.Test - name: Run AutoExtern Tests - run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" Source/AutoExtern.Test + if: "!inputs.soak_test" + run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/AutoExtern.Test/coverlet.runsettings Source/AutoExtern.Test - name: Run DafnyRuntime Tests - run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" Source/DafnyRuntime.Tests - - uses: actions/upload-artifact@v3 - if: always() + if: "!inputs.soak_test" + run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyRuntime.Tests/coverlet.runsettings Source/DafnyRuntime.Tests + - uses: actions/upload-artifact@v4 + if: always() && !inputs.soak_test with: name: unit-test-results-${{ matrix.os }} path: | diff --git a/.gitignore b/.gitignore index 1a7a01535f0..bb6f6c501c0 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,6 @@ Source/DafnyCore/Scanner.cs.old Source/DafnyRuntime/MSBuild_Logs/ TestResults/ -Test/.lit_test_times.txt Docs/OnlineTutorial/DocumentationTransducer.exe Docs/OnlineTutorial/DocumentationTransducer.pdb @@ -58,6 +57,9 @@ Source/.vs # Generated by VS Code .vscode/* +# Generated by Dafny tooling +/Source/DafnyStandardLibraries/build + # Generated by Java tools (gradle/javac/etc) /Source/DafnyRuntime/DafnyRuntimeJava/.gradle @@ -70,3 +72,22 @@ package-lock.json docs/dev/*.fix docs/dev/*.feat docs/dev/*.break + +Source/IntegrationTests/TestFiles/LitTests/LitTest/server/*.bvd +/Source/IntegrationTests/TestFiles/LitTests/LitTest/separate-verification/Inputs/wrappers3.doo +/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/replaceables/complex/Build1 +/Source/IntegrationTests/TestFiles/LitTests/LitTest/separate-verification/assumptions-lib +/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/log.smt2 +/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/model +Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/*.html +Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/**/*.html +Source/IntegrationTests/TestFiles/LitTests/LitTest/**/*.dtr +/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/projectFile/libs/usesLibrary-lib +/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/projectFile/libs/usesLibrary.doo +Source/IntegrationTests/TestFiles/LitTests/LitTest/**/*.deps.json +Source/IntegrationTests/TestFiles/LitTests/LitTest/**/*.csproj +/Source/IntegrationTests/TestFiles/LitTests/LitTest/pythonmodule/multimodule/PythonModule2 +/Source/IntegrationTests/TestFiles/LitTests/LitTest/pythonmodule/singlemodule/dafnysource/PythonModule1 +/Source/DafnyCore/Prelude/DafnyPrelude.bpl +/Source/DafnyCore/Prelude/Sequences.bpl +/venv diff --git a/.gitmodules b/.gitmodules index 90ba01b0d54..e69de29bb2d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "Test/libraries"] - path = Test/libraries - url = https://github.com/dafny-lang/libraries.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7dff2b96253..f4cdfba04b2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,5 +4,6 @@ repos: - id: dotnet-format name: dotnet-format language: system - entry: dotnet tool run dotnet-format --folder -w --include + entry: dotnet format whitespace Source/Dafny.sln -v:d --include + exclude: 'Source/DafnyCore/Scanner.cs|Source/DafnyCore/Parser.cs|.git/modules/Source/boogie|Source/DafnyCore/GeneratedFromDafny|Source/DafnyCore.Test/GeneratedFromDafny|Source/DafnyRuntime/DafnyRuntimeSystemModule.cs' types_or: ["c#"] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8ab3a05586..ddaf0541814 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,17 +30,17 @@ If you want to propose code changes for the Dafny project, please note: - To propose code changes, use the standard Github multi-user project process, which is described for Dafny on the [wiki](https://github.com/dafny-lang/dafny/wiki/Setting-up-a-development-copy-of-Dafny). If your change is user-visible, then part of the PR should be corresponding changes to the -[`RELEASE_NOTES.md`](../RELEASE_NOTES.md) file and to the +[`RELEASE_NOTES.md`](./RELEASE_NOTES.md) file (by following [this](./docs/dev/README.md)) and to the [Dafny Reference Manual](./docs/DafnyRef). Any PR should have tests that check whether the bug-fix fixes the problem addressed or that the new functionality is properly working. - - Dafny's integration tests are in [`Test`](../Test). PRs that fix issues reported on GitHub should include a test in [`Test/git-issues/`](../Test/git-issues/). + - Dafny's integration tests are in [this directory](Source/IntegrationTests/TestFiles/LitTests/LitTest). PRs that fix issues reported on GitHub should include a test under [`git-issues/`](Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/). - Each `.dfy` file in `Test/` is a test, with a [`lit`](https://llvm.org/docs/CommandGuide/lit.html) header describing how to run it and a `.expect` file indicating the expected output. See [`Test/README.md`](../Test/README.md) for more info on running Dafny' integration tests. + Each `.dfy` file in this directory is a test, with a [`lit`](https://llvm.org/docs/CommandGuide/lit.html) header describing how to run it and a `.expect` file indicating the expected output. See [this README.md file](Source/IntegrationTests/TestFiles/LitTests/LitTest/README.md) for more info on running Dafny' integration tests. - - Dafny's unit tests are in various `*.Test` directories in [`Source`](../Source). + - Dafny's unit tests are in various `*.Test` directories in [`Source`](./Source). Our CI is configured to run all tests when you create a PR. To run tests locally, use `dotnet test Source/Dafny.sln` (but note that running the tests for our compiler backends requires installing lots of additional software). @@ -74,7 +74,7 @@ After doing these steps once, for other PRs, one only needs to re-run deep check - If some required tests fail, investigate and push more commits, but know that each CI run takes a lot of time, so try to fix the problem with as few commits as possible (ideally 1) It is also possible you find some tests that fail randomly, in that case, re-run them and report to the team. Avoid using the re-run all failed jobs here, because it will re-run _all_ jobs, so select them individually instead. - Have someone approve the PR and merge it (or auto-merge). -- Once the PR with the fix to the CI is merged to master, go to https://github.com/dafny-lang/dafny/actions/workflows/deep-tests.yml +- Once the PR with the fix to the CI is merged to master, go to https://github.com/dafny-lang/dafny/actions/workflows/nightly-build.yml - Select "Run workflow..." - Select master - Wait for this new run to succeed. @@ -85,7 +85,7 @@ Subsequent CI runs should pick up the successful `deep-tests` run and make the ` ### How can I write portions of Dafny in Dafny itself? Since https://github.com/dafny-lang/dafny/pull/2399, it is possible to add \*.dfy files next to other source files. -The plugin `dafny-msbuild` takes all the dafny files and compiles them into a single file `Source/DafnyCore/obj/Debug/net6.0/GeneratedFromDafny.cs` +The plugin `dafny-msbuild` takes all the dafny files and compiles them into a single file `Source/DafnyCore/obj/Debug/net8.0/GeneratedFromDafny.cs` that is automatically included in the build process. This file contains also the Dafny run-time in C#. One example of such file is `Source/DafnyCore/AST/Formatting.dfy`, and you can use it as a starting point. @@ -101,3 +101,9 @@ For example, `Formatting.dfy` ### What is the release process? You can find a description of the release process in [docs/dev/RELEASE.md](https://github.com/dafny-lang/dafny/blob/master/docs/dev/RELEASE.md). + +### Backwards compatibility + +Dafny is still changing and backwards incompatible changes may be made. Any backwards compatibility breaking change must be easy to adapt to, such as by adding a command line option. In the future, we plan to add a `dafny migrate` command which should support migrating any Dafny codebase from the previous to the current CLI version. + +As rule, Dafny features must be marked as deprecated, including migration instructions, at least one release before they are removed. diff --git a/Makefile b/Makefile index fbd17ba228c..5d526d9ffd5 100644 --- a/Makefile +++ b/Makefile @@ -1,71 +1,132 @@ -DIR=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) +# Run these tasks even if eponymous files or folders exist +.PHONY: test-dafny exe + +DIR=$(realpath $(dir $(firstword $(MAKEFILE_LIST)))) default: exe all: exe refman exe: - (cd ${DIR} ; dotnet build Source/Dafny.sln ) ## includes parser + (cd "${DIR}" ; dotnet build Source/Dafny.sln ) ## includes parser + +format-dfy: + (cd "${DIR}"/Source/DafnyCore ; make format) + (cd "${DIR}"/Source/DafnyStandardLibraries ; make format) + +dfy-to-cs: + (cd "${DIR}"/Source/DafnyCore ; bash DafnyGeneratedFromDafny.sh) + +dfy-to-cs-exe: dfy-to-cs + (cd "${DIR}" ; dotnet build Source/Dafny.sln ) + +dfy-to-cs-noverify: + (cd "${DIR}"/Source/DafnyCore ; bash DafnyGeneratedFromDafny.sh --no-verify) + +dfy-to-cs-noverify-exe: dfy-to-cs-noverify exe boogie: ${DIR}/boogie/Binaries/Boogie.exe tests: - (cd ${DIR}; dotnet test Source/IntegrationTests) + (cd "${DIR}"; dotnet test Source/IntegrationTests) + +# make test name= +# make test name= update=true to update the test +# make test name= build=false don't build the solution +test: + @DIR="$(DIR)" name="$(name)" update="$(update)" build="$(build)" bash scripts/test.sh + +# Run Dafny on an integration test case directly in the folder itself. +# make test-dafny name= action="run ..." [build=false] +test-dafny: + @name="$(name)" DIR="$(DIR)" action="$(action)" NO_BUILD=$$( [ "${build}" = "false" ] && echo "true" || echo "false" ) bash scripts/test-dafny.sh tests-verbose: - (cd ${DIR}; dotnet test --logger "console;verbosity=normal" Source/IntegrationTests ) + (cd "${DIR}"; dotnet test --logger "console;verbosity=normal" Source/IntegrationTests ) ${DIR}/boogie/Binaries/Boogie.exe: - (cd ${DIR}/boogie ; dotnet build -c Release Source/Boogie.sln ) + (cd "${DIR}"/boogie ; dotnet build -c Release Source/Boogie.sln ) refman: exe - make -C ${DIR}/docs/DafnyRef + make -C "${DIR}"/docs/DafnyRef refman-release: exe - make -C ${DIR}/docs/DafnyRef release + make -C "${DIR}"/docs/DafnyRef release z3-mac: - mkdir -p ${DIR}Binaries/z3/bin + mkdir -p "${DIR}"/Binaries/z3/bin wget https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02/z3-4.12.1-x64-macos-11-bin.zip unzip z3-4.12.1-x64-macos-11-bin.zip rm z3-4.12.1-x64-macos-11-bin.zip wget https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02/z3-4.8.5-x64-macos-11-bin.zip unzip z3-4.8.5-x64-macos-11-bin.zip rm z3-4.8.5-x64-macos-11-bin.zip - mv z3-* ${DIR}/Binaries/z3/bin/ - chmod +x ${DIR}/Binaries/z3/bin/z3-* + mv z3-* "${DIR}"/Binaries/z3/bin/ + chmod +x "${DIR}"/Binaries/z3/bin/z3-* z3-mac-arm: - mkdir -p ${DIR}Binaries/z3/bin + mkdir -p "${DIR}"/Binaries/z3/bin wget https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02/z3-4.12.1-arm64-macos-11-bin.zip unzip z3-4.12.1-arm64-macos-11-bin.zip rm z3-4.12.1-arm64-macos-11-bin.zip wget https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02/z3-4.8.5-x64-macos-11-bin.zip unzip z3-4.8.5-x64-macos-11-bin.zip rm z3-4.8.5-x64-macos-11-bin.zip - mv z3-* ${DIR}/Binaries/z3/bin/ - chmod +x ${DIR}/Binaries/z3/bin/z3-* + mv z3-* "${DIR}"/Binaries/z3/bin/ + chmod +x "${DIR}"/Binaries/z3/bin/z3-* z3-ubuntu: - mkdir -p ${DIR}Binaries/z3/bin + mkdir -p "${DIR}"/Binaries/z3/bin wget https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02/z3-4.12.1-x64-ubuntu-20.04-bin.zip unzip z3-4.12.1-x64-ubuntu-20.04-bin.zip rm z3-4.12.1-x64-ubuntu-20.04-bin.zip wget https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2023-08-02/z3-4.8.5-x64-ubuntu-20.04-bin.zip unzip z3-4.8.5-x64-ubuntu-20.04-bin.zip rm z3-4.8.5-x64-ubuntu-20.04-bin.zip - mv z3-* ${DIR}/Binaries/z3/bin/ - chmod +x ${DIR}/Binaries/z3/bin/z3-* + mv z3-* "${DIR}"/Binaries/z3/bin/ + chmod +x "${DIR}"/Binaries/z3/bin/z3-* format: - dotnet tool run dotnet-format -w -s error Source/Dafny.sln --exclude DafnyCore/Scanner.cs --exclude DafnyCore/Parser.cs --exclude DafnyCore/GeneratedFromDafny.cs --exclude DafnyCore/GeneratedFromDafnyRust.cs + dotnet format whitespace Source/Dafny.sln --exclude Source/DafnyCore/Scanner.cs --exclude Source/DafnyCore/Parser.cs --exclude boogie --exclude Source/DafnyCore/GeneratedFromDafny/* --exclude Source/DafnyCore.Test/GeneratedFromDafny/* --exclude Source/DafnyRuntime/DafnyRuntimeSystemModule.cs clean: - (cd ${DIR}; cd Source; rm -rf Dafny/bin Dafny/obj DafnyDriver/bin DafnyDriver/obj DafnyRuntime/obj DafnyRuntime/bin DafnyServer/bin DafnyServer/obj DafnyPipeline/obj DafnyPipeline/bin DafnyCore/obj DafnyCore/bin) - (cd ${DIR} ; dotnet build Source/Dafny.sln -v:q --nologo -target:clean ) - make -C ${DIR}/Source/DafnyCore -f Makefile clean - (cd ${DIR}/Source/Dafny && rm -rf Scanner.cs Parser.cs obj ) - (cd ${DIR}/Source/DafnyRuntime/DafnyRuntimeJava; ./gradlew clean) - make -C ${DIR}/docs/DafnyRef clean - (cd ${DIR}; cd Source; rm -rf Dafny/bin Dafny/obj DafnyDriver/bin DafnyDriver/obj DafnyRuntime/obj DafnyRuntime/bin DafnyServer/bin DafnyServer/obj DafnyPipeline/obj DafnyPipeline/bin DafnyCore/obj DafnyCore/bin) + (cd "${DIR}"; cd Source; rm -rf Dafny/bin Dafny/obj DafnyDriver/bin DafnyDriver/obj DafnyRuntime/obj DafnyRuntime/bin DafnyServer/bin DafnyServer/obj DafnyPipeline/obj DafnyPipeline/bin DafnyCore/obj DafnyCore/bin) + (cd "${DIR}" ; dotnet build Source/Dafny.sln -v:q --nologo -target:clean ) + make -C "${DIR}"/Source/DafnyCore -f Makefile clean + (cd "${DIR}"/Source/Dafny && rm -rf Scanner.cs Parser.cs obj ) + (cd "${DIR}"/Source/DafnyRuntime/DafnyRuntimeJava; ./gradlew clean) + make -C "${DIR}"/docs/DafnyRef clean + (cd "${DIR}"; cd Source; rm -rf Dafny/bin Dafny/obj DafnyDriver/bin DafnyDriver/obj DafnyRuntime/obj DafnyRuntime/bin DafnyServer/bin DafnyServer/obj DafnyPipeline/obj DafnyPipeline/bin DafnyCore/obj DafnyCore/bin) echo Source/*/bin Source/*/obj + +bumpversion-test: + node ./Scripts/bump_version_number.js --test 1.2.3 + +update-cs-module: + (cd "${DIR}"; cd Source/DafnyRuntime; make update-system-module) + +update-rs-module: + (cd "${DIR}"; cd Source/DafnyRuntime/DafnyRuntimeRust; make update-system-module) + +update-go-module: + (cd "${DIR}"; cd Source/DafnyRuntime/DafnyRuntimeGo; make update-system-module) + +update-runtime-dafny: + (cd "${DIR}"; cd Source/DafnyRuntime/DafnyRuntimeDafny; make update-go) + +pr-nogeneration: format-dfy format update-runtime-dafny update-cs-module update-rs-module update-go-module + +update-standard-libraries: + (cd "${DIR}"; cd Source/DafnyStandardLibraries; make update-binary) + +# `make pr` will bring you in a state suitable for submitting a PR +# - Builds the Dafny executable +# - Use the build to convert core .dfy files to .cs +# - Rebuilds the Dafny executable with this .cs files +# - Apply dafny format on all dfy files +# - Apply dotnet format on all cs files except the generated ones +# - Rebuild the Go and C# runtime modules as needed. +pr: exe dfy-to-cs-exe pr-nogeneration + +# Same as `make pr` but useful when resolving conflicts, to take the last compiled version of Dafny first +pr-conflict: dfy-to-cs-exe dfy-to-cs-exe pr-nogeneration diff --git a/README.md b/README.md index 85b5d451cc3..065b4134367 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# Dafny + [![Build and Test](https://github.com/dafny-lang/dafny/workflows/Build%20and%20Test/badge.svg)](https://github.com/dafny-lang/dafny/actions?query=workflow%3A%22Build+and+Test%22) [![Gitter](https://badges.gitter.im/dafny-lang/community.svg)](https://gitter.im/dafny-lang/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) Dafny is a **verification-ready programming language**. As you type in your program, Dafny's verifier constantly looks over your shoulder, flags any errors, shows you counterexamples, and congratulates you when your code matches your specifications. When you're done, Dafny can **compile your code to C#, Go, Python, Java, or JavaScript** (more to come!), so it can integrate with your existing workflow. @@ -19,29 +21,29 @@ This github site contains these materials: * the [issue tracker](https://github.com/dafny-lang/dafny/issues) * the wiki, including [frequently asked questions](https://github.com/dafny-lang/dafny/wiki/FAQ) -Documentation about the dafny language and tools is located +Documentation about the Dafny language and tools is located [here](https://dafny-lang.github.io/dafny). A reference manual is available both [online](https://dafny-lang.github.io/dafny/DafnyRef/DafnyRef) and as [pdf](https://github.com/dafny-lang/dafny/blob/master/docs/DafnyRef/out/DafnyRef.pdf). (A LaTeX version can be produced if needed.) -# Community +## Community -You can ask questions about Dafny on [Stack Overflow](https://stackoverflow.com/questions/tagged/dafny) or participate in general discussion on Dafny's [![Gitter](https://badges.gitter.im/dafny-lang/community.svg)](https://gitter.im/dafny-lang/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge). +Feel free to report issues here on GitHub or to ask for questions on our :speech_balloon: [Zulip](https://dafny.zulipchat.com/) channel. -# Try Dafny +## Try Dafny The easiest way to try out Dafny is to [install Dafny on your own machine in Visual Studio Code](https://github.com/dafny-lang/dafny/wiki/INSTALL#visual-studio-code) and follow along with the [Dafny tutorial](https://dafny-lang.github.io/dafny/OnlineTutorial/guide). You can also [download and install](https://github.com/dafny-lang/dafny/wiki/INSTALL#install-the-binaries) the Dafny CLI if you prefer to work from the command line. -# Read more +## Read more Here are some ways to get started with Dafny: * 4-part course on the _Basics of specification and verification of code_: - - Lecture 0: [Pre- and postconditions](https://youtu.be/oLS_y842fMc) (19:08) - - Lecture 1: [Invariants](https://youtu.be/J0FGb6PyO_k) (20:56) - - Lecture 2: [Binary search](https://youtu.be/-_tx3lk7yn4) (21:14) - - Lecture 3: [Dutch National Flag algorithm](https://youtu.be/dQC5m-GZYbk) (20:33) + * Lecture 0: [Pre- and postconditions](https://youtu.be/oLS_y842fMc) (19:08) + * Lecture 1: [Invariants](https://youtu.be/J0FGb6PyO_k) (20:56) + * Lecture 2: [Binary search](https://youtu.be/-_tx3lk7yn4) (21:14) + * Lecture 3: [Dutch National Flag algorithm](https://youtu.be/dQC5m-GZYbk) (20:33) * New overview article: [Accessible Software Verification with Dafny](https://www.computer.org/csdl/mags/so/2017/06/mso2017060094-abs.html), IEEE Software, Nov/Dec 2017 * [Online tutorial](https://dafny-lang.github.io/dafny/OnlineTutorial/guide), focusing mostly on simple imperative programs * [3-page tutorial notes](http://leino.science/papers/krml233.pdf) with examples (ICSE 2013) @@ -49,7 +51,7 @@ Here are some ways to get started with Dafny: * Language reference for the [Dafny type system](http://leino.science/papers/krml243.html), which also describes available expressions for each type * [Cheatsheet](https://docs.google.com/document/d/1kz5_yqzhrEyXII96eCF1YoHZhnb_6dzv-K3u79bMMis/edit?pref=2&pli=1): basic Dafny syntax on two pages * Dafny Reference Manual [[html](https://dafny-lang.github.io/dafny/DafnyRef/DafnyRef)] [[pdf](https://github.com/dafny-lang/dafny/blob/master/docs/DafnyRef/out/DafnyRef.pdf)] -* [Dafny libraries](https://github.com/dafny-lang/libraries), a standard library of useful Dafny functions and lemmas +* [Dafny libraries](Source/DafnyStandardLibraries/README.md), a standard library of useful Dafny functions and lemmas * [Dafny Power User](http://leino.science/dafny-power-user) * Videos at [Verification Corner](https://www.youtube.com/channel/UCP2eLEql4tROYmIYm5mA27A) * [Blog](https://dafny.org/blog) @@ -63,18 +65,18 @@ The language itself draws pieces of influence from: * ML (like the module system, and its functions and inductive datatypes), and * Coq and VeriFast (like the ability to include co-inductive datatypes and being able to write inductive and co-inductive proofs). -# External contributions +## External contributions * [Haskell-to-Dafny translator](http://www.doc.ic.ac.uk/~dcw/h2d.cgi), by Duncan White * [Vim-loves-Dafny mode](https://github.com/mlr-msft/vim-loves-dafny) for vim, by Michael Lowell Roberts * [Boogie-Friends Emacs mode](https://github.com/boogie-org/boogie-friends) -# Contributors +## Contributors Information and instructions for potential contributors are provided [here](CONTRIBUTING.md). -# License +## License Dafny itself is licensed under the MIT license. (See `LICENSE.txt` in the root directory for details.) The subdirectory `Source/Dafny/Coco` contains third -party material; see `Source/Dafny/Coco/LICENSE.txt` for more details. +party material; see `Source/DafnyCore/Coco/LICENSE.txt` for more details. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index bdc52048954..120b43fdc64 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,642 @@ See [docs/dev/news/](docs/dev/news/). +# 4.9.1 + +## New features + +- Introduce the attributes {:isolate} and {:isolate "paths} that simplify the verification of an assertion by introducing additional verification jobs. {:isolate} can be applied to `assert`, `return` and `continue` statements. When using `{:isolate_assertions}` or `--isolate-assertions`, each return statement now creates a separate verification job for each ensures clause. Previously all ensures clauses where verified in a single job, for all return statements. (https://github.com/dafny-lang/dafny/pull/5832) + +- Fill in matching patterns for the quantifiers introduced by automatic induction to represent the induction hypothesis. Suppress the generation of the induction hypothesis if no such matching patterns are found. Enhance tooltips accordingly. This feature is added to stabilize verification, but by sometimes not generating induction hypotheses, some automatic proofs may no longer go through. For backward compatibility, use an explicit `{:induction ...}` where `...` is the list of variables to use for the induction-hypothesis quantifier. Additionally, use a `{:nowarn}` attribute to suppress any warning about lack of matching patterns. + + Improve the selection of induction variables. + + Allow codatatype equality in matching patterns and as a focal predicate for extreme predicates. + + More specifically: + + * If a lemma bears `{:induction x, y, z}`, where `x, y, z` is a subset of the lemma's parameters (in the same order + that the lemma gives them), then an induction hypothesis (IH) is generated. The IH quantifies over the + given variables. + + For an instance-member lemma, the variables may include the implicit `this` parameter. + + For an extreme lemma, the IH generated is the for corresponding prefix lemma, and the given variables may + include the implicit parameter `_k`. + + If good matching patterns are found for the quantifier, then these are indicated in tooltips. + If no patterns are found, then a warning is generated; except, if the lemma bears `{:nowarn}`, then only + an informational message is given. + + * If a lemma bears `{:induction}` or `{:induction true}`, then a list of induction variables is determined heuristically. + + If the list is empty, then a warning message is generated and no IH is generated. If the list is nonempty, + an IH is generated and the list of variables is indicated in a tooltip. + + If good matching patterns are found for the quantifier, then these are indicated in tooltips. + If no patterns are found, then a warning is generated; except, if the lemma bears {:nowarn}, then only + an informational message is given. + + * If a lemma bears `{:induction false}`, then no IH is generated. + + * If a lemma bears an `:induction` attribute other than those listed above, then an error is generated. + + * If a lemma bears no `:induction` attribute, and the `--manual-lemma-induction` flag is present, then no IH is generated. + + * Otherwise, a list of induction variables is determined heuristically. + + If this list is empty, then no IH is generated and no warning/info is given. + + If the list is nonempty, then the machinery looks for matching patterns for the IH quantifier. If none are + found, then no IH is generated. An informational message is generated, saying which candidate variables were + used and saying that no matching patterns were found. + + If patterns are found, then an IH is generated, the list of variables and the patterns are indicated in tooltips, + and the patterns are used with the IH quantifier. + + The pattern search can be overriden by providing patterns explicitly using the `{:inductionTrigger}` attribute. + This attribute has the same syntax as the `{:trigger}` attribute. Using an empty list of triggers restores + Dafny's legacy behavior (no triggers for lemma induction hypotheses). + (https://github.com/dafny-lang/dafny/pull/5835) + +- Accept `decreases to` and `nonincreases to` expressions with 0 LHSs and/or 0 RHSs, and allow parentheses to be omitted when there is 1 LHS and 1 RHS. (https://github.com/dafny-lang/dafny/pull/5891) + +- Allow forall statements in statement expressions (https://github.com/dafny-lang/dafny/pull/5894) + +- When using `--isolate-assertions` or `{:isolate_assertions}`, a separate assertion batch will be generated per pair of return statement and ensures clause. (https://github.com/dafny-lang/dafny/pull/5917) + +## Bug fixes + +- `{:only}` on members only affects verification on the current file. (https://github.com/dafny-lang/dafny/pull/5730) + +- Fixed a bug that caused "hide *" and "reveal *" not to work when used in statement expressions, + after a variable assignment occurred in the same expression. + (https://github.com/dafny-lang/dafny/pull/5781) + +- Fix malformed Boogie in function override checks (https://github.com/dafny-lang/dafny/pull/5875) + +- Fix soundness issue where the verifier had assumed properties of `this` already during the first phase of a constructor (https://github.com/dafny-lang/dafny/pull/5876) + +- Don't assume type information before binding variables (for let expressions and named function results) (https://github.com/dafny-lang/dafny/pull/5877) + +- Enable using reveal statement expression inside witness expressions (https://github.com/dafny-lang/dafny/pull/5882) + +- Fix formatting of var by statements (https://github.com/dafny-lang/dafny/pull/5927) + +- Fix bugs that occur when using `{:extern}` to export members (https://github.com/dafny-lang/dafny/pull/5939) + +- Fixed a bug that would cause the symbol verification tasks to be done multiple times when using module refinement (https://github.com/dafny-lang/dafny/pull/5967) + +- Map range requires equality for enclosing type to support equality (https://github.com/dafny-lang/dafny/pull/5972) + +- Improved code navigation for datatype update expressions (https://github.com/dafny-lang/dafny/pull/5986) + +# 4.9.0 + +## New features + +- Added opaque blocks to the language. Opaque blocks enable improving verification performance. See the documentation for more details. (https://github.com/dafny-lang/dafny/pull/5761) + +- By blocks ("... by { ... }") are now available for assert statements, call statements, and the three types of assignments (:=, :-, :|). Also, by blocks should now be more intuitive since they enable proving any assertions on the left-hand side of the 'by', not just the 'outermost' one. For example, previously `assert 3 / x == 1 by { assume x == 3; }` could still give a possible division by zero error, but now it won't. (https://github.com/dafny-lang/dafny/pull/5779) + +- Use --suggest-proof-refactoring to be alerted of function definitions, which have no contribution to a method's proof, and facts, which are only used once in a proof. (https://github.com/dafny-lang/dafny/pull/5812) + +- Support for [top-level @-attributes](https://dafny.org/latest/DafnyRef/DafnyRef#sec-at-attributes) (https://github.com/dafny-lang/dafny/pull/5825) + +## Bug fixes + +- Enable abstract imports to work well with match expression that occur in specifications (https://github.com/dafny-lang/dafny/pull/5808) + +- Fix a bug that prevented using reveal statement expressions in the body of a constant. (https://github.com/dafny-lang/dafny/pull/5823) + +- Improve performance of `dafny verify` for large applications, by removing memory leaks (https://github.com/dafny-lang/dafny/pull/5827) + +- Green gutter icons cover constants without RHS (https://github.com/dafny-lang/dafny/pull/5841) + +# 4.8.1 + +## New features + +- feat: allow type parameters of `newtype` declarations + feat: support optional `witness` clause of constraint-less `newtype` declarations + feat: show tool tips for auto-completed type parameters + feat: show tool tips for inferred `(==)` characteristics + fix: Don't let `newtype` well-formedness checking affect witness checking (fixes ##5520) + fix: Check the emptiness status of constraint-less `newtype` declarations (fixes #5521) + (https://github.com/dafny-lang/dafny/pull/5495) + +- New feature: model extractor + + ### CLI option + + The `dafny verify` command now has an option `--extract:`, where (just like for the various print options) `` is allowed to be `-` to denote standard output. + + ### Extract mechanism + + Upon successful verification, the new extract mechanism visits the AST of the given program. For any module marked with `{:extract}`, the extract-worthy material from the module is output. The output declarations will be in the same order as they appear textually in the module (in particular, the fact that module-level Dafny declarations are collected in an internal class `_default` has no bearing on the output order). + + Three kinds of declarations are extract-worthy: + + * A type declaration `A` that bears an attribute `{:extract_name B}` is extracted into a Boogie type declaration `type B _ _ _;`. + + The definition of the type is ignored. (The intended usage for an extracted type is that the Dafny program give a definition for the type, which goes to show the existence of such a type.) + + * A function declaration `F(x: X, y: Y): Z` that bears an attribute `{:extract_name G}` is extracted into a Boogie function declaration `function G(x: X, y: Y): Z;`. + + The body of the Dafny function is ignored. (The intended usage for an extracted function is that the Dafny program give a definition for the function, which goes to show the existence of such a function.) + + * A lemma declaration `L(x: X, y: Y) requires P ensures Q` that bears an attribute `{:extract_pattern ...}` or an attribute `{:extract_used_by ...}` is extracted into a Boogie `axiom`. The axiom has the basic form `axiom (forall x: X, y: Y :: P ==> Q);`. + + If the lemma has an attribute `{:extract_used_by F}`, then the axiom will be emitted into the `uses` clause of the Boogie function generated for Dafny function `F`. + + If the lemma has no in-parameters, the axiom is just `P ==> Q`. + + If the lemma has in-parameters, then any attribute `{:extract_pattern E, F, G}` adds a matching pattern `{ E, F, G }` to the emitted quantifier. Also, any attribute `{:extract_attribute "name", E, F, G}` adds an attribute `{:name E, F, G}` to the quantifier. + + ### Expressions + + The pre- and postconditions of extracted lemmas turn into analogous Boogie expressions, and the types of function/lemma parameters and bound variables are extracted into analogous Boogie types. The intended usage of the extract mechanism is that these expressions and types do indeed have analogous Boogie types. + + At this time, only a limited set of expressions and types are supported, but more can be added in the future. + + Any `forall` and `exists` quantifiers in expressions are allowed to use `:extract_pattern` and `:extract_attribute` attributes, as described above for lemmas. + + Some extracted expressions are simplified. For example, `true && !!P` is simplified to `P`. + + ### Soundness + + The Dafny program that is used as input for the extraction is treated like any other Dafny program. The intended usage of the extraction mechanism is to prove parts of the axiomatization in `DafnyPrelude.bpl` to be logically consistent. Whether or not the extracted Boogie declarations meet this goal depends on the given Dafny program. For example, if the given Dafny program formalizes sequences in terms of maps and formalizes maps in terms of sequences, then the extraction probably does not provide guarantees of consistency. + (https://github.com/dafny-lang/dafny/pull/5621) + +- Dafny-to-Rust: `{:test}` methods generate `#[test]` wrappers in Rust that can be invoked using `cargo test`. + Similarly, `{:rust_cfg_test}` on modules generates a `#[cfg(test)]` in the resulting rust module. + (https://github.com/dafny-lang/dafny/pull/5676) + +## Bug fixes + +- Allow hiding instance member using a static reference + +- Enable using "hide *" in the context of a recursive function + +- Support for double constant initialization in Dafny-to-Rust (https://github.com/dafny-lang/dafny/pull/5642) + +- Support for enumerating datatypes in the Rust backend (https://github.com/dafny-lang/dafny/pull/5643) + +- Tail-Recursion for the Dafny-to-Rust compiler (https://github.com/dafny-lang/dafny/pull/5647) + +- The new resolver (accessible using `--type-system-refresh`) can now handle revealing instance functions using a static receiver, as it is the case for the current resolver (https://github.com/dafny-lang/dafny/pull/5760) + +# 4.8.0 + +## New features + +- Introduce `hide` statements that enable hiding the body of a function at a particular proof location, which allows simplifying the verification of that proof in case the body of the function is not needed for the proof. `Hide` statements make the opaque keyword on functions obsolete. (https://github.com/dafny-lang/dafny/pull/5562) + +- Let the command `measure-complexity` output which verification tasks performed the worst in terms of resource count. Output looks like: + ... + Verification task on line 8 in file measure-complexity.dfy consumed 9984 resources + Verification task on line 7 in file measure-complexity.dfy consumed 9065 resources + ... + (https://github.com/dafny-lang/dafny/pull/5631) + +- Enable the option `--enforce-determinism` for the commands `resolve` and `verify` (https://github.com/dafny-lang/dafny/pull/5632) + +- Method calls get an optional by-proof that hides the precondtion and its proof (https://github.com/dafny-lang/dafny/pull/5662) + +## Bug fixes + +- Clarify error location of inlined `is` predicates. (https://github.com/dafny-lang/dafny/pull/5587) + +- Optimize the compilation of single-LHS assignment statements to allow the RHS to be a deeply nested expression. This solves a problem in compiling to Java, since `javac` does not deal gracefully with nested lambda expressions. (https://github.com/dafny-lang/dafny/pull/5589) + +- Crash when compiling an empty source file while including testing code (https://github.com/dafny-lang/dafny/pull/5638) + +- Let the options --print-mode=NoGhostOrIncludes and --print-mode=NoIncludes work (https://github.com/dafny-lang/dafny/pull/5645) + +- Verification in the IDE now works correctly when declaring nested module in a different file than their parent. (https://github.com/dafny-lang/dafny/pull/5650) + +- Fix NRE that would occur when using --legacy-data-constructors (https://github.com/dafny-lang/dafny/pull/5655) + +# 4.7.0 + +## New features + +- Add the option --find-project that given a Dafny file traverses up the file tree until it finds a Dafny project that includes that path. This is useful when developing a particular file and doing CLI invocations as part of your development workflow. + +- Improved error reporting when verification times out or runs out of resources, so that when using `--isolate-assertions`, the error message points to the problematic assertion. (https://github.com/dafny-lang/dafny/pull/5281) + +- Support newtypes based on map and imap (https://github.com/dafny-lang/dafny/pull/5175) + +- To enable smoothly working with multiple projects inside a single repository, Dafny now allows using a Dafny project file as an argument to `--library`. When using `dafny verify`, Dafny ensures that any dependencies specified through a project are verified as well, unless using the flag `--dont-verify-dependencies`. (https://github.com/dafny-lang/dafny/pull/5297) + +- Experimental Dafny-to-Rust compiler development + - Supports emitting code even if malformed with option `--emit-uncompilable-code`. + - Supports for immutable collections and operators + (https://github.com/dafny-lang/dafny/pull/5081) + +- Allow for plugins to add custom request handlers to the language server. (https://github.com/dafny-lang/dafny/pull/5161) + +- Deprecated the unicode-char option (https://github.com/dafny-lang/dafny/pull/5302) + +- Warn when passing a Dafny source file to `--library` (https://github.com/dafny-lang/dafny/pull/5313) + +- Add support for "translation records", which record the options used when translating library code. + * `--translation-record` - Provides a `.dtr` file from a previous translation of library code. Can be specified multiple times. + * `--translation-record-output` - Customizes where to write the translation record for the current translation. Defaults to the output directory. + Providing translation records is necessary to handle options such as `--outer-module` that affect how code is translated. + (https://github.com/dafny-lang/dafny/pull/5346) + +- The new `decreases to` expression makes it possible to write an explicit assertion equivalent to the internal check Dafny does to prove that a loop or recursive call terminates. (https://github.com/dafny-lang/dafny/pull/5367) + +- The new `assigned` expression makes it possible to explicitly assert that a variable, constant, out-parameter, or object field is definitely assigned. (https://github.com/dafny-lang/dafny/pull/5501) + +- Greatly reduced the size of generated code for the backends: C#, Python, GoLang and JavaScript. + +- Introduce additional warnings that previously only appeared when running the `dafny audit` command. Two warnings are as follows: + - Emit a warning when exporting a declaration that has requires clauses or subset type inputs + - Emit a warning when importing a declaration that has ensures clauses or subset type outputs +Those two can be silenced with the flag `--allow-external-contracts`. A third new warning occurs when using bodyless functions marked with `{:extern}`, and can be silenced using the option `--allow-external-function`. + +- Enable project files to specify another project file as a base, which copies all configuration from that base file. More information can be found in the reference manual. + +## Bug fixes + +- Fix a common memory leak that occurred when doing verification in the IDE that could easily consume gigabytes of memory. + +- Fix bugs that could cause the IDE to become unresponsive + +- Improve the performance of the Dafny IDE by fixing bugs in its caching code + +- No longer reuse SMT solver processes between different document version when using the IDE, making the IDE verification behavior more inline to that of the CLI + +- Fix bugs that caused Dafny IDE internal errors (https://github.com/dafny-lang/dafny/issues/5355, https://github.com/dafny-lang/dafny/pull/5543, https://github.com/dafny-lang/dafny/pull/5548) + +- Fix bugs in the Dafny IDEs code navigation and renaming features when working with definition that are not referred to. + +- Fix a code navigation bug that could occur when navigating to and from module imports +- +- Fix a code navigation bug that could occur when navigating to and from explicit types of variables + (https://github.com/dafny-lang/dafny/pull/5419) + +- Let the IDE no longer show diagnostics for projects for which all files have been closed (https://github.com/dafny-lang/dafny/pull/5437) + +- Fix bug that could lead to an unresponsive IDE when working with project files (https://github.com/dafny-lang/dafny/pull/5444) + +- Fix bugs in Dafny library files that could cause them not to work with certain option values, such as --function-syntax=3 + +- Fix a bug that prevented building Dafny libraries for Dafny projects that could verify without errors. + +- Reserved module identifiers correctly escaped in GoLang (https://github.com/dafny-lang/dafny/pull/4181) + +- Fix a soundness issue that could be triggered by calling ensures fresh in the post-condition of a constructor (https://github.com/dafny-lang/dafny/pull/4700) + +- Ability to cast a datatype to its trait when overriding functions (https://github.com/dafny-lang/dafny/pull/4823) + +- Fix crash that could occur when using a constructor in a match pattern where a tuple was expected (https://github.com/dafny-lang/dafny/pull/4860) + +- No longer emit an incorrect internal error while waiting for verification message (https://github.com/dafny-lang/dafny/pull/5209) + +- More helpful error messages when read fields not mentioned in reads clauses (https://github.com/dafny-lang/dafny/pull/5262) + +- Check datatype constructors for bad type-parameter instantiations (https://github.com/dafny-lang/dafny/pull/5278) + +- Avoid name clashes with Go built-in modules (https://github.com/dafny-lang/dafny/pull/5283) + +- Invalid Python code for nested set and map comprehensions (https://github.com/dafny-lang/dafny/pull/5287) + +- Stop incorrectly emitting the error message "Dafny encountered an internal error while waiting for this symbol to verify" (https://github.com/dafny-lang/dafny/pull/5295) + +- Rename the `dafny generate-tests` option `--coverage-report` to `--expected-coverage-report` (https://github.com/dafny-lang/dafny/pull/5301) + +- Stop giving an incorrect warning about a missing {:axiom} clause on an opaque constant. (https://github.com/dafny-lang/dafny/pull/5306) + +- No new resolver crash when datatype update expressions are partially resolved (https://github.com/dafny-lang/dafny/pull/5331) + +- Optional pre-type won't cause a crash anymore (https://github.com/dafny-lang/dafny/pull/5369) + +- Unguarded enumeration of bound variables in set and map comprehensions (https://github.com/dafny-lang/dafny/pull/5402) + +- Reference the correct `this` after removing the tail call of a function or method (https://github.com/dafny-lang/dafny/pull/5474) + +- Apply name mangling to datatype names in Python more often (https://github.com/dafny-lang/dafny/pull/5476) + +- Only guard `this` when eliminating tail calls, if possible (https://github.com/dafny-lang/dafny/pull/5524) + +- Compiled disjunctive nested pattern matching no longer crashing (https://github.com/dafny-lang/dafny/pull/5572) + +# 4.6.0 + +## New features + +- Add a check to `dafny run` that notifies the user when a value that was parsed as a user program argument, and which occurs before a `--` token, starts with `--`, since this indicates they probably mistyped a Dafny option name. (https://github.com/dafny-lang/dafny/pull/5097) + +- Add an option --progress that can be used to track the progress of verification. (https://github.com/dafny-lang/dafny/pull/5150) + +- Add the attribute `{:isolate_assertions}`, which does the same as `{:vcs_split_on_every_assert}`. Deprecated `{:vcs_split_on_every_assert}` (https://github.com/dafny-lang/dafny/pull/5247) + +## Bug fixes + +- (soundness issue) Twostate predicate now check if their not new arguments are allocated in the previous heap (https://github.com/dafny-lang/dafny/pull/4844) + +- Add uniform checking of type characteristics in refinement modules (https://github.com/dafny-lang/dafny/pull/5146) + +- Fixed links associated with the standard libraries. (https://github.com/dafny-lang/dafny/pull/5176) + +- fix: Disable the "erase datatype wrappers" optimization if the datatype implements any traits. + feat: Allow the "erase datatype wrappers" optimization if the only fields in the datatype are ghost fields. + (https://github.com/dafny-lang/dafny/pull/5234) + +- Fix the default string value emitted for JavaScript (https://github.com/dafny-lang/dafny/pull/5239) + +# 4.5.0 + +## New features + +- Add the option `--include-test-runner` to `dafny translate`, to enable getting the same result as `dafny test` when doing manual compilation. (https://github.com/dafny-lang/dafny/pull/3818) + +- - Fix: verification in the IDE no longer fails for iterators + - Fix: the IDE now provides feedback when verification fails to run, for example due to a bad solver path + - Fix: let the IDE correctly use the solver-path option when it's specified in a project file + - Feat: improve the order of verification diagnostics emitted by the Dafny CLI, so that they now always follow the line order of the program. + (https://github.com/dafny-lang/dafny/pull/4798) + +- - Add an option `--filter-position` to the `dafny verify` command. The option filters what gets verified based on a source location. The location is specified as a file path suffix, optionally followed by a colon and a line number. For example, `dafny verify dfyconfig.toml --filter-position=source1.dfy:5` will only verify things that range over line 5 in the file `source1.dfy`. In combination with ``--isolate-assertions`, individual assertions can be verified by filtering on the line that contains them. When processing a single file, the filename can be skipped, for example: `dafny verify MyFile.dfy --filter-position=:23` + - Add an option `--filter-symbol` to the `dafny verify` command, that only verifies symbols whose fully qualified name contains the given argument. For example, `dafny verify dfyconfig.toml --filter-symbol=MyModule` will verify everything inside `MyModule`. + - The option `--boogie-filter` has been removed in favor of --filter-symbol + (https://github.com/dafny-lang/dafny/pull/4816) + +- Add a `json` format to those supported by `--log-format` and `/verificationLogger`, for producing thorough, machine readable logs of verification results. (https://github.com/dafny-lang/dafny/pull/4951) + +- - Flip the behavior of `--warn-deprecation` and change the name to `--allow-deprecation`, so the default is now false, which is standard for boolean options. + - When using `--allow-deprecation`, deprecated code is shown using tooltips in the IDE, and on the CLI when using `--show-tooltips`. + - Replace the option `--warn-as-error` with the option `--allow-warnings`. The new option, when false, the default value, causes Dafny to stop generating executable output and return a failure exit code, when warnings occur in the program. Contrary to the previous `--warn-as-error` option, warnings are always reported as warnings. + - During development, users must use `dafny run --allow-warnings` if they want to run their Dafny code when it contains warnings. + - If users have builds that were passing with warnings, they have to add `--allow-warnings` to allow them to still pass. + - If users upgrade to a new Dafny version, and are not using `--allow-warnings`, and do not want to migrate off of deprecated features, they will have to use `--allow-deprecation`. + - When using the legacy CLI, the option /warningsAsErrors now has the behavior of --allow-warnings=false + - A doo file that was created using `--allow-warnings` causes a warning if used by a consumer that does not use it. + (https://github.com/dafny-lang/dafny/pull/4971) + +- The new `{:contradiction}` attribute can be placed on an `assert` statement to indicate that it forms part of an intentional proof by contradiction and therefore shouldn't be warned about when `--warn-contradictory-assumptions` is turned on. (https://github.com/dafny-lang/dafny/pull/5001) + +- Function and method parameters and return types, and datatype constructor arguments, can now have attributes. By default, there are no attributes that Dafny recognizes in these positions, but custom back-ends can use this feature to get extra information from the source files. (https://github.com/dafny-lang/dafny/pull/5032) + +- Under the CLI option `--general-newtypes`, the base type of a `newtype` declaration can now be (`int` or `real`, as before, or) `bool`, `char`, or a bitvector type. + + `as` and `is` expressions now support more types than before. In addition, run-time type tests are supported for `is` expressions, provided type parameters are injective (as was already required) and provided the constraints of any subset type or newtype is compilable. Note, although both `as` and `is` allow many more useful cases than before, using `--general-newtypes` will also forbid some unusual cases that were previously allowed. Any such case that is now forbidden can still be done by doing the `as`/`is` via `int`. + (https://github.com/dafny-lang/dafny/pull/5061) + +- Allow newtype declarations to be based on set/iset/multiset/seq. (https://github.com/dafny-lang/dafny/pull/5133) + +## Bug fixes + +- Fixed crash caused by cycle in type declaration (https://github.com/dafny-lang/dafny/pull/4471) + +- Fix resolution of unary minus in new resolver (https://github.com/dafny-lang/dafny/pull/4737) + +- The command line and the language server now use the same counterexample-related Z3 options. (https://github.com/dafny-lang/dafny/pull/4792) + +- Dafny no longer crashes when required parameters occur after optional ones. (https://github.com/dafny-lang/dafny/pull/4809) + +- Use defensive coding to prevent a crash in the IDE that could occur in the context of code actions. (https://github.com/dafny-lang/dafny/pull/4818) + +- Fix null-pointer problem in new resolver (https://github.com/dafny-lang/dafny/pull/4875) + +- Fixed a crash that could occur when a case body of a match that was inside a loop, had a continue or break statement. (https://github.com/dafny-lang/dafny/pull/4894) + +- Compile run-time constraint checks for newtypes in comprehensions (https://github.com/dafny-lang/dafny/pull/4919) + +- Fix null dereference in constant-folding invalid string-indexing expressions (https://github.com/dafny-lang/dafny/pull/4921) + +- Check for correct usage of type characteristics in specifications and other places where they were missing. + + This fix will cause build breaks for programs with missing type characteristics (like `(!new)` and `(0)`). Any such error message is accompanied with a hint about what type characterics need to be added where. + (https://github.com/dafny-lang/dafny/pull/4928) + +- Initialize additional fields in the AST (https://github.com/dafny-lang/dafny/pull/4930) + +- Fix crash when a function/method with a specification is overridden in an abstract type. (https://github.com/dafny-lang/dafny/pull/4954) + +- Fix crash for lookup of non-existing member in new resolver (https://github.com/dafny-lang/dafny/pull/4955) + +- Fix: check that subset-type variable's type is determined (resolver refresh). + Fix crash in verifier when there was a previous error in determining subset-type/newtype base type. + Fix crash in verifier when a subset type has no explicit `witness` clause and has a non-reference trait as its base type. + (https://github.com/dafny-lang/dafny/pull/4956) + +- The `{:rlimit N}` attribute, which multiplied `N` by 1000 before sending it to Z3, is deprecated in favor of the `{:resource_limit N}` attribute, which can accept string arguments with exponential notation for brevity. The `--resource-limit` and `/rlimit` flags also now omit the multiplication, and the former allows exponential notation. (https://github.com/dafny-lang/dafny/pull/4975) + +- Allow a datatype to depend on traits without being told "datatype has no instances" (https://github.com/dafny-lang/dafny/pull/4997) + +- Don't consider `:= *` to be a definite assignment for non-ghost variables of a `(00)` type (https://github.com/dafny-lang/dafny/pull/5024) + +- Detect the same ghost usage in initializing assignments as in other expressions. The effect of this fix is to allow more iset/imap comprehensions to be compiled. + + Also, report errors if the LHS of `:=` in compiled `map`/`imap` comprehensions contains ghosts. + (https://github.com/dafny-lang/dafny/pull/5041) + +- Escape names of nested modules in C# and Java (https://github.com/dafny-lang/dafny/pull/5049) + +- A parent trait that is a reference type can now be named via `import opened`. + + Implicit conversions between a datatype and its parent traits no longer crashes the verifier. + + (Dis)equality expressions of a (co)datatype and its parent traits no longer crashes the verifier. + (https://github.com/dafny-lang/dafny/pull/5058) + +- Fixed support for newtypes as sequence comprehension lengths in Java (https://github.com/dafny-lang/dafny/pull/5065) + +- Don't emit an error message for a `function-by-method` with unused type parameters. (https://github.com/dafny-lang/dafny/pull/5068) + +- The syntax of a predicate definition must always include parentheses. (https://github.com/dafny-lang/dafny/pull/5069) + +- Termination override check for certain non-reference trait implementations (https://github.com/dafny-lang/dafny/pull/5087) + +- Malformed Python code for some functions involving lambdas (https://github.com/dafny-lang/dafny/pull/5093) + +- Let verifier understand opaque function overrides, supporting both when the overridden function is opaque and non-opaque. Revealing such a function for one overriding type has the effect of revealing it for all overriding types. + + Also, forbid the case where a function is opaque in a parent trait and the function override is not opaque. (Previously, this had caused a crash.) + (https://github.com/dafny-lang/dafny/pull/5111) + +# 4.4.0 + +## New features + +- Reads clauses on method declarations are now supported when the `--reads-clauses-on-methods` option is provided. + The `{:concurrent}` attribute now verifies that the `reads` and `modifies` clauses are empty instead of generating an auditor warning. + (https://github.com/dafny-lang/dafny/pull/4440) + +- Added two new options, `--warn-contradictory-assumptions` and `--warn-redundant-assumptions`, to detect potential problems with specifications that indicate that successful verification may be misleading. These options are currently hidden because they may occasionally produce false positives in cases where proofs are so trivial that the solver never does work on them. (https://github.com/dafny-lang/dafny/pull/4542) + +- Verification of the `{:concurrent}` attribute on methods now allows non-empty `reads` and `modifies` clauses with the `{:assume_concurrent}` attribute. (https://github.com/dafny-lang/dafny/pull/4563) + +- Implemented support for workspace/symbol request to allow IDE navigation by symbol. (https://github.com/dafny-lang/dafny/pull/4619) + +- The new `--verification-coverage-report` flag to `dafny verify` creates an HTML report highlighting which portions of the program were and were not necessary for verification. The format is the same as for `dafny generate-tests --coverage-report` and files from the two commands can be merged. (https://github.com/dafny-lang/dafny/pull/4625) + +- Built-in types such as the `nat` subset type, tuples, arrows, and arrays are now pre-compiled into each backend's runtime library, + instead of emitted on every call to `dafny translate`, to avoid potential duplicate definitions when translating components separately. + (https://github.com/dafny-lang/dafny/pull/4658) + +- The new `--only-label` option to `merge-coverage-reports` includes only one category of highlighting in the output. For example, merging coverage reports from test generation and verification using the option `--only-label NotCovered` will highlight only the regions not covered by either testing or verification. (https://github.com/dafny-lang/dafny/pull/4673) + +- The Dafny distribution now includes standard libraries, available with the `--standard-libraries` option. + See https://github.com/dafny-lang/dafny/blob/master/Source/DafnyStandardLibraries/README.md for details. + (https://github.com/dafny-lang/dafny/pull/4678) + +- Introduce replaceable modules, which can be used to help define Dafny applications that translate to multiple target languages. (https://github.com/dafny-lang/dafny/pull/4681) + +- The new `--coverage-report` flag to `dafny run` and `dafny test` creates an HTML report highlighting which portions of the program were executed at runtime. (https://github.com/dafny-lang/dafny/pull/4755) + +- Enable turning nonlinear arithmetic on or off on a per-module basis, using the attribute `{:disable-nonlinear-arithmetic}`, + which optionally takes the value false to enable nonlinear arithmetic. + (https://github.com/dafny-lang/dafny/pull/4773) + +- Let the IDE provide code navigation in situations where the program parses but has resolution errors. Note that this only works for modules whose dependency tree does not have errors, or modules who contain errors themselves, but not for modules whose dependencies contain errors. (https://github.com/dafny-lang/dafny/pull/4855) + +## Bug fixes + +- Ensures that computing the set of values or items of map can only be done if the type of the range supports equality. (https://github.com/dafny-lang/dafny/pull/1373) + +- Subset type decl's witness correctly taken into account (https://github.com/dafny-lang/dafny/pull/3792) + +- Added a comprehensive test for test generation and fixed a bug that prevented test generation to process function-by-method declarations (https://github.com/dafny-lang/dafny/pull/4406) + +- Optimized memory consumption of test generation by reusing the Boogie AST of the target Dafny program. (https://github.com/dafny-lang/dafny/pull/4530) + +- Fix a bug that prevented certain types of lemma to be verified in the IDE (https://github.com/dafny-lang/dafny/pull/4607) + +- Dot completion now works on values the type of which is a type synonym. (https://github.com/dafny-lang/dafny/pull/4635) + +- Fix a case where the document symbol API would return incorrect data when working on a file with parse errors (https://github.com/dafny-lang/dafny/pull/4675) + +- Emit less nested target code in match-case expressions (nice for readability, and necessary for Java) (https://github.com/dafny-lang/dafny/pull/4683) + +- Ghost diagnostics are now correctly updated when they become empty (https://github.com/dafny-lang/dafny/pull/4693) + +- Enable verification options that are configured in a Dafny project file, to be picked up by the Dafny language server (https://github.com/dafny-lang/dafny/pull/4703) + +- Prevent double-counting of covered/uncovered lines in test coverage reports (https://github.com/dafny-lang/dafny/pull/4710) + +- fix: correction of type inference for default expressions (https://github.com/dafny-lang/dafny/pull/4724) + +- The new type checker now also supports static reveals for instance functions (https://github.com/dafny-lang/dafny/pull/4733) + +- Resolve :- expressions with void outcomes in new resolver (https://github.com/dafny-lang/dafny/pull/4734) + +- Crash in the resolver on type parameters of opaque functions in refined modules (https://github.com/dafny-lang/dafny/pull/4768) + +- Fix error messages being printed after their context snippets (https://github.com/dafny-lang/dafny/pull/4787) + +- Override checks no longer crashing when substituting type parameters and equality (https://github.com/dafny-lang/dafny/pull/4812) + +- Removed one cause of need for restarting the IDE. (https://github.com/dafny-lang/dafny/pull/4833) + +- The Python compiler emits reserved names for datatypes (https://github.com/dafny-lang/dafny/pull/4843) + +# 4.3.0 + +## New features + +- Add support for the Find References LSP request + - Returned references may be incomplete when not using project mode + (https://github.com/dafny-lang/dafny/pull/2320) + +- Improve scalability of inlining for test generation and generate coverage information with respect to the original Dafny source (https://github.com/dafny-lang/dafny/pull/4255) + +- Support generating of tests targeting path-coverage of the entire program and tests targeting call-graph-sensitive block coverage (referred to as Branch coverage) (https://github.com/dafny-lang/dafny/pull/4326) + +- Add support for Rename LSP request + - Support requires enabling project mode and defining a Dafny project file + (https://github.com/dafny-lang/dafny/pull/4365) + +- Make verification in the IDE more responsive by starting verification after translating the required module to Boogie, instead of first translating all modules that could be verified. (https://github.com/dafny-lang/dafny/pull/4378) + +- The Dafny IDE now has improved behavior when working with a Dafny file that's part of a Dafny project. A Dafny file is part of a project if a `dfyconfig.toml` can be found somewhere in the file's path hierarchy, such as in the same folder or in the parent folder. A `dfyconfig.toml` can specify which Dafny options to use for that project, and can specify which Dafny files are part of the project. By default, the project will include all .dfy files reachable from the folder in which the `dfyconfig.toml` resides. Project related features of the IDE are: + - Whenever one file in the project is opened, diagnostics for all files in the Dafny project are shown. When including a file with errors that's part of the same project, the message "the included file contains errors" is no longer shown. Instead, the included file's errors are shown directly. + - If any file in the project is changed, diagnostics for all files in the project are updated. Without a project, changing an included file will not update diagnostics for the including file until the including file is also changed. + - The find references feature (also added in this release), works better in files that are part of a project, since only then can it find references that are inside files that include the current file. + - The assisted rename feature (also added in this release), only works for files that are part of a project. + - When using a project file, it is no longer necessary to use include directives. In the previous version of Dafny, it was already the case that the Dafny CLI, when passed a Dafny project file, does not require include directives to process the Dafny program. The same now holds for the Dafny IDE when working with Dafny files for which a project file can be found. + - If any file in the project is resolved, all files in the project are resolved. Opening a file in a project that's already resolved means the opened file is resolved instantly. + - The IDE's memory consumption stays the same regardless of how many files in a project are opened. Without a project, the IDE increases it's memory usage for each open file. + + Try out the IDE's project support now by creating an empty `dfyconfig.toml` file in the root of your project repository. + (https://github.com/dafny-lang/dafny/pull/4435) + +- Prior to generating tests, Dafny now checks the targeted program for any features that test generation does not support or any misuse of test generation specific attributes. + Any such issues are reported to the user. + (https://github.com/dafny-lang/dafny/pull/4444) + +- Added documentation of the generate-tests command to the reference manual (https://github.com/dafny-lang/dafny/pull/4445) + +- When two modules in the same scope have the same name, Dafny now reports an error that contains the location of both modules. (https://github.com/dafny-lang/dafny/pull/4499) + +- - The Dafny IDE will now report errors that occur in project files. + - The Dafny IDE will now shown a hint diagnostic at the top of Dafny project files, that says which files are referenced by the project. + (https://github.com/dafny-lang/dafny/pull/4539) + +## Bug fixes + +- Triggers warnings correclty converted into errors with --warn-as-errors (https://github.com/dafny-lang/dafny/pull/3358) + +- Allow JavaScript keywords as names of Dafny modules (https://github.com/dafny-lang/dafny/pull/4243) + +- Support odd characters in pathnames for Go (https://github.com/dafny-lang/dafny/pull/4257) + +- Allow Dafny filenames compiled to Java to start with a digit (https://github.com/dafny-lang/dafny/pull/4258) + +- Parser now supports files with a lot of consecutive single-line comments (https://github.com/dafny-lang/dafny/pull/4261) + +- Gutter icons more robust (https://github.com/dafny-lang/dafny/pull/4287) + +- Unresolved abstract imports no longer crash the resolver (https://github.com/dafny-lang/dafny/pull/4298) + +- Make capitalization of target Go code consistent (https://github.com/dafny-lang/dafny/pull/4310) + +- Refining an abstract module with a class with an opaque function no longer crashes (https://github.com/dafny-lang/dafny/pull/4315) + +- Dafny can now produce coverage reports indicating which parts of the program are expected to be covered by automatically generated tests. + The same functionality can be used to report other forms of coverage. + (https://github.com/dafny-lang/dafny/pull/4325) + +- Fix issue that would prevent the IDE from correctly handling default export sets (https://github.com/dafny-lang/dafny/pull/4328) + +- Allow function-syntax and other options with a custom default to be overridden in `dfyconfig.toml` (https://github.com/dafny-lang/dafny/pull/4357) + +- There were two differences between verification in the IDE and the CLI, one small and one tiny, which would only become apparent for proofs that Z3 had trouble verifying. The small difference has been resolved, while the tiny one still persists, so the IDE and CLI should behave almost equivalently now, including resource counts, on most code. (https://github.com/dafny-lang/dafny/pull/4374) + +- Old command line interface for test generation is no longer supported, all calls should use dafny generate-tests (https://github.com/dafny-lang/dafny/pull/4385) + +- Fixed a bug leading to stack overflow during counterexample extraction on some programs. (https://github.com/dafny-lang/dafny/pull/4392) + +- Ability to use .Key as a constant name in datatypes and classes (https://github.com/dafny-lang/dafny/pull/4394) + +- Existential assertions are now printed correctly (https://github.com/dafny-lang/dafny/pull/4401) + +- When a symbol such as a method is not given a name, the error given by Dafny now shows where this problem occurs. (https://github.com/dafny-lang/dafny/pull/4411) + +- Fix flickering and incorrect results in the verification status bar, and improve responsiveness of verification diagnostics (https://github.com/dafny-lang/dafny/pull/4413) + +- Significantly improve IDE responsiveness for large projects, preventing it from appearing unresponsive or incorrect. Previously, for projects of a certain size, the IDE would not be able to keep up with incoming changes made by the user, possibly causing it to never catch up and appearing frozen or showing outdated results. (https://github.com/dafny-lang/dafny/pull/4419) + +- Declarations with {:only} ensure that other declarations aren't displayed as verified in the gutter icons (https://github.com/dafny-lang/dafny/pull/4432) + +- Fix issues that could cause the IDE status bar to show incorrect information (https://github.com/dafny-lang/dafny/pull/4454) + +- When verification times out, only show a red underline on the name of the verifiable for which verification timed out, instead of under its whole definition. (https://github.com/dafny-lang/dafny/pull/4477) + +- Prevent the IDE from becoming completely unresponsive after certain kinds of parse errors would occur. (https://github.com/dafny-lang/dafny/pull/4495) + +- Support all types of options in the Dafny project file (dfyconfig.toml) (https://github.com/dafny-lang/dafny/pull/4506) + +- Fix an issue that would cause some types of errors and other diagnostics not to appear in the IDE, if they appeared in files other than the active one. (https://github.com/dafny-lang/dafny/pull/4513) + +- Fix an IDE issue that would lead to exceptions when using module export declarations and making edits in imported modules that were re-exported (https://github.com/dafny-lang/dafny/pull/4556) + +- Fix a leak in the IDE that would cause it to become less responsive over time. (https://github.com/dafny-lang/dafny/pull/4570) + # 4.2.0 ## New features diff --git a/Scripts/bump_version_number.js b/Scripts/bump_version_number.js new file mode 100644 index 00000000000..23e39fbf2cb --- /dev/null +++ b/Scripts/bump_version_number.js @@ -0,0 +1,369 @@ +#!/bin/env node +// Every comment of this file starting with //# comes from the file ../docs/dev/VERSIONBUMP.md +// and vice-versa + +//# After each release, we change the version number by first running +//# python3 Scripts/prepare_release.py NEXT_VERSION set-next-version +//# where NEXT_VERSION is the version of the last release of the format +//# `major.minor.patch` and the patch has been increased by 1. This script +//# has for effect to change the version number in `Source/Directory.Build.props`. +//# Then, many things in Dafny that depend on this version number have to be +//# updated. +//# This file is an exhaustive list of everything that needs to be updated +//# whenever the version number changes. All these steps are +//# executable by running +//# ./Scripts/bump_version_number.js NEW_VERSION +//# and `make bumpversion-test` run by the CI on each release +//# verifies that this file is in sync with that script. + +const fs = require('fs'); +const { promisify } = require('util'); +const {exec, spawn} = require('child_process'); +const execAsync = promisify(exec); +const minutes = 60*1000; +const versionFile = "Source/Directory.Build.props"; +// Change the working directory to Dafny +process.chdir(__dirname + "/.."); +const prefixLinkedComment = "//# "; +var existingVersion = ""; +//# Assuming `` to be `Source/IntegrationTests/TestFiles/LitTests/LitTest`, +//# perform the following: +const TestDirectory = "Source/IntegrationTests/TestFiles/LitTests/LitTest"; +var {version, testMode} = processArgs(process.argv.slice(2)); // Remove "node" and the script's name. +if(testMode) { + test(); +} else { + synchronizeRepositoryWithNewVersionNumber(); +} + +async function synchronizeRepositoryWithNewVersionNumber() { + existingVersion = await getVersionNumber(); + if(version === false) { + version = existingVersion; + } else { + await bumpVersionNumber(version); + } + //# * Compile Dafny to ensure you have the right version number. + await execute("make exe"); + + //# * Compile the standard libraries and update their binaries which are checked in + await executeWithTimeout("make -C Source/DafnyStandardLibraries update-binary", 50*minutes); + + //# * Recompile Dafny so that standard libraries are in the executable. + await execute("make exe"); + + //# * In the test directory `Source/IntegrationTests/TestFiles/LitTests/LitTest`, + await execute( + //# * Rebuild `pythonmodule/multimodule/PythonModule1.doo` from `pythonmodule/multimodule/dafnysource/helloworld.dfy` + `bash Scripts/dafny build -t:lib ${TestDirectory}/pythonmodule/multimodule/dafnysource/helloworld.dfy -o ${TestDirectory}/pythonmodule/multimodule/PythonModule1.doo`, + //# * Rebuild `pythonmodule/nestedmodule/SomeNestedModule.doo` from `pythonmodule/nestedmodule/dafnysource/SomeNestedModule.dfy` + `bash Scripts/dafny build -t:lib ${TestDirectory}/pythonmodule/nestedmodule/dafnysource/SomeNestedModule.dfy -o ${TestDirectory}/pythonmodule/nestedmodule/SomeNestedModule.doo`, + //# * Rebuild `gomodule/multimodule/test.doo` from `gomodule/multimodule/dafnysource/helloworld.dfy` + `bash Scripts/dafny build -t:lib ${TestDirectory}/gomodule/multimodule/dafnysource/helloworld.dfy -o ${TestDirectory}/gomodule/multimodule/test.doo`); + + //# * Search for `dafny_version = ` in checked-in `.dtr` files of the `` + //# and update the version number. + var files = await findFiles(TestDirectory, ".*\\.dtr"); + assert_eq(files.length != 0, true); + for(let file of files) { + //# Except for the file NoGood.dtr which is not valid. + if(file.endsWith("NoGood.dtr")) { + continue; + } + //# Except for WrongDafnyVersion.dtr as well. + if(file.endsWith("WrongDafnyVersion.dtr")) { + continue; + } + await replaceInFile(/dafny_version = "\d+\.\d+\.\d+/, `dafny_version = "${version}`, + `${TestDirectory}/${file}`); + } + + //# * In `Source/DafnyRuntime/DafnyRuntimeJava/build.gradle`, search for `version = ` and update the version number + await replaceInFile(/version = '(\d+\.\d+\.\d+)/, `version = '${version}`, + `Source/DafnyRuntime/DafnyRuntimeJava/build.gradle`, existingVersion); + + //# * In `Source/DafnyRuntime/DafnyRuntimePython/pyproject.toml`, search for `version = ` and update the version number + await replaceInFile(/version = "(\d+\.\d+\.\d+)"/, `version = "${version}"`, + `Source/DafnyRuntime/DafnyRuntimePython/pyproject.toml`, existingVersion); + + //# * Update `comp/separate-compilation/translation-records/InvalidFormat.dfy.expect` by updating the version number after `by Dafny ` + await replaceInFile(/by Dafny \d+\.\d+\.\d+/, `by Dafny ${version}`, + `${TestDirectory}/comp/separate-compilation/translation-records/InvalidFormat.dfy.expect`); + + //# * Update the Dafny runtime version number by searching for `DafnyRuntime-` and updating the version right afterwards, in the files `DafnyPipeline.csproj` and `DafnyRuntime.csproj` + await replaceInFile(/DafnyRuntime-(\d+\.\d+\.\d+)\.jar/, `DafnyRuntime-${version}.jar`, + `Source/DafnyPipeline/DafnyPipeline.csproj`, existingVersion); + await replaceInFile(/DafnyRuntime-(\d+\.\d+\.\d+)\.jar/, `DafnyRuntime-${version}.jar`, + `Source/DafnyRuntime/DafnyRuntime.csproj`, existingVersion); +} + +// Returns the current version number +async function getVersionNumber() { + var versionFileContent = await fs.promises.readFile(versionFile, "utf-8"); + var version = readVersionNumber(versionFileContent); + if(version === false) { + throw "Could not find version number in " + versionFile; + } + return version; +} + +// Returns the version number from the given file content (see const versionFile) +function readVersionNumber(versionFileContent) { + var versionMatch = versionFileContent.match(/(\d+\.\d+\.\d+)<\/VersionPrefix>/); + if(versionMatch != null) { + return versionMatch[1]; + } + return false; +} + +// Increases the patch of the given version number +async function bumpVersionNumber(version) { + console.log(`Bumping version number to ${version}`); + var versionFileContent = await fs.promises.readFile(versionFile, "utf-8"); + newVersionFileContent = replaceVersionNumber(versionFileContent, version); + if(testMode) { + if(newVersionFileContent === versionFileContent) { + throw "Expected to find " + version + " in " + versionFile; + } + console.log("Would have updated " + versionFile + " to " + newVersionFileContent); + return; + } + await fs.promises.writeFile(versionFile, newVersionFileContent); +} + +// Replace the given version number in the given file that has the old version number +function replaceVersionNumber(versionFileContent, newVersion) { + return versionFileContent.replace(/\d+\.\d+\.\d+<\/VersionPrefix>/, `${newVersion}`); +} + + +// Spawn async +function spawnAsync(commandAndArgs) { + return new Promise((resolve, reject) => { + // Regular expression to match quoted arguments and non-quoted parts + const regex = /'[^']*'|"[^"]*"|\S+/g; + + // Extract all matches and clean up quotes + const parts = commandAndArgs.match(regex).map(part => { + // Remove surrounding quotes (both single and double) if they exist + if ((part.startsWith('"') && part.endsWith('"')) || + (part.startsWith("'") && part.endsWith("'"))) { + return part.slice(1, -1); + } + return part; + }); + + const command = parts[0]; + const args = parts.slice(1); + const childProcess = spawn(command, args); + + childProcess.stdout.on('data', (data) => { + process.stdout.write(`| ${data}`); + }); + + childProcess.stderr.on('data', (data) => { + process.stdout.write(`! ${data}`); + }); + + childProcess.on('close', (code) => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`Process exited with code ${code}`)); + } + }); + }); +} + +// Execute each command in its arguments +async function execute() { + var commands = arguments; + for(let i = 0; i < commands.length; i++) { + let command = commands[i]; + if(testMode) { + console.log("Would have run " + command); + } else { + console.log("Running " + command); + await spawnAsync(command); + } + } +} + +// Find files in the given directory, recursively +// Returns them with the relative path from the given directory +async function findFiles(directory, regexString) { + var files = []; + var regexFilter = new RegExp(regexString); + var dir = await fs.promises.opendir(directory); + for await (const dirent of dir) { + if(dirent.isDirectory()) { + var subFiles = await findFiles(directory + "/" + dirent.name, regexString); + files = files.concat(subFiles.map(f => dirent.name + "/" + f)); + } else if(dirent.isFile() && regexFilter.test(dirent.name)) { + files.push(dirent.name); + } + } + return files; +} + +// Execute the given comment, with the possibility of timing out +function executeWithTimeout(command, timeout) { + if(testMode) { + console.log("Would have run " + command + " with a timeout of " + timeout); + return new Promise((resolve, reject) => { resolve(); }); + } + // Instead of having an async closure, we are going to return the promise directly + // so that it can be fullfilled either way. + return new Promise((resolve, reject) => { + var finished = false; + var childProcess = undefined; + var timeoutId = setTimeout(() => { + if(!finished) { + finished = true; + if(typeof childProcess != undefined) { + childProcess.kill(); + } + resolve({ok: false, log: `Timeout after ${timeout/1000}s`}); + } + }, timeout); + console.log(`Running ${command} with timeout of ${timeout/1000}s`) + childProcess = exec(command, (error, stdout, stderr) => { + clearTimeout(timeoutId); + finished = true; + if(error) { + resolve({ok: false, log: (error + "\n" + stderr + "\n" + stdout).trim()}); + } else { + resolve({ok: true, log: stdout, answer: undefined}); + } + }); + // Add these two handlers + childProcess.stdout.on('data', (data) => { + process.stdout.write(`| ${data}`); + }); + + childProcess.stderr.on('data', (data) => { + process.stdout.write(`! ${data}`); + }); + }); +} + +// Find the regex in the given file, and replaces it with the given replacement. +async function replaceInFile(regex, replacement, fileName, testExistingVersion = null) { + var fileContent = await fs.promises.readFile(fileName, 'utf-8'); + // Replace "Dafny \d.\d.\d" by the new version + var previous = fileContent.match(regex); + if(previous == null ) { + throw `Could not find ${regex} in ${fileName}`; + } + var newContent = fileContent.replace(regex, replacement); + if(testMode) { + if(testExistingVersion != null) { + assert_eq(previous[1], testExistingVersion); + } + console.log(`Would have replaced ${previous[0]} with ${replacement} in ${fileName}`); + if(newContent == fileContent) { + throw "Error in replaceInFile, replacement did not do anything"; + } + } else { + await fs.promises.writeFile(fileName, newContent); + } +} + +// Process the arguments. +// If the first argument is --test, then we set the test mode where +// we are going to read this file and verify that all lines of ../docs/dev/VERSIONBUMP.md +// became a comment in this file. +function processArgs(args) { + var testMode, version; + testMode = args[0] == "--test"; + if (testMode) { + args = args.slice(1); + } + + // If another argument is given, it's a version number that we can update + // If there is one argument, it must be the new version number. + var version = false; + if (args.length > 0) { + version = args[0]; + if (!version.match(/^\d+\.\d+\.\d+$/)) { + console.log(`Invalid version number ${version}`); + process.exit(1); + } + } + return {version, testMode}; +} + +////////////////// Testing part to ensure this script is in sync ///////////// + +async function test() { + await ensureTakesIntoAccountAllReleaseInstructions(); + assert_eq(readVersionNumber( + "\n 1.2.3\n"), "1.2.3"); + assert_eq(readVersionNumber( + " 1.2"), false); + assert_eq(replaceVersionNumber( + "\n 1.2.3\n", "1.2.4"), + "\n 1.2.4\n"); + await synchronizeRepositoryWithNewVersionNumber(); // Test mode +} + +// Step to ensure all the public release instructions are taken into account +async function ensureTakesIntoAccountAllReleaseInstructions() { + if(version === false) { + version = await getVersionNumber(); + } else { + await bumpVersionNumber(version); + } + // Read this file itself, and ensure every line of docs/dev/RELEASE.md + // appears somewhere in this file in its own line, possibly trimmed + var thisScript = await fs.promises.readFile(__filename, 'utf-8'); + var publicBumpFile = 'docs/dev/VERSIONBUMP.md'; + var publicReleaseInstructions = await fs.promises.readFile(publicBumpFile, 'utf-8'); + var lines = publicReleaseInstructions.split("\n"); + var thisScriptLines = thisScript.split("\n"). + map(line => line.trim()). + filter(line => line.startsWith(prefixLinkedComment)). + map(line => line.substring(prefixLinkedComment.length).trim()); + var missingLines = []; + var lineNumber = 1; + for(let line of lines) { + trimmedLine = line.trim(); + if(trimmedLine.length > 0 && !trimmedLine.startsWith("#") && thisScriptLines.indexOf(trimmedLine) === -1) { + missingLines.push("//# " + line); + } + lineNumber++; + } + if(missingLines.length > 0) { + for(let line of missingLines) { + console.log(line); + } + console.log(`------------------------------------`); + console.log(`${missingLines.length} lines of ${publicBumpFile} were not found in ${__filename} ^^`); + console.log(`Please update either file.`); + process.exit(1); + } + + // Now verify that each line of thisScriptLines is in the publicBumpFile + var extraLines = []; + var linesTrimmed = lines.map(line => line.trim()); + for(let line of thisScriptLines) { + if(linesTrimmed.indexOf(line) === -1) { + extraLines.push(line); + } + } + if(extraLines.length > 0) { + for(let line of extraLines) { + console.log(line); + } + console.log(`------------------------------------`); + console.log(`${extraLines.length} comment lines of ${__filename} were not found in ${publicBumpFile} ^^`); + console.log(`Please update either file.`); + process.exit(1); + } +} + +function assert_eq(got, expected) { + if(got != expected) { + throw "Expected " + expected + ", got " + got; + } +} \ No newline at end of file diff --git a/Scripts/package.py b/Scripts/package.py index f543d37715c..aaffbfe4e20 100755 --- a/Scripts/package.py +++ b/Scripts/package.py @@ -161,7 +161,6 @@ def build(self): if path.exists(self.buildDirectory): shutil.rmtree(self.buildDirectory) run(["make", "--quiet", "clean"]) - self.run_publish("DafnyLanguageServer") self.run_publish("DafnyServer") self.run_publish("DafnyRuntime", "netstandard2.0") self.run_publish("DafnyRuntime", "net452") diff --git a/Scripts/prepare_release.py b/Scripts/prepare_release.py index 4c8cbf0b630..bb625bb63c0 100755 --- a/Scripts/prepare_release.py +++ b/Scripts/prepare_release.py @@ -322,6 +322,18 @@ def _push_release_branch(self): self.REMOTE, f"{self.release_branch_path}:{self.release_branch_path}", check=True) + def set_next_version(self): + assert_one(f"Is {self.version} a valid version number?", + self._parse_vernum) + assert_one(f"Can we find `{self.build_props_path}`?", + self._build_props_file_exists) + assert_one(f"Can we parse `{self.build_props_path}`?", + self._build_props_file_parses) + assert_one(f"Can we create a section for `{self.version}` in `{self.release_notes_md_path}`?", + self._version_number_is_fresh) + run_one(f"Updating `{self.build_props_path}`...", + self._update_build_props_file) + # Still TODO: # - Run deep test as part of release workflow @@ -375,10 +387,11 @@ def prepare(self): progress("Done!") progress() - DEEPTESTS_URL = "https://github.com/dafny-lang/dafny/actions/workflows/deep-tests.yml" + DEEPTESTS_URL = "https://github.com/dafny-lang/dafny/actions/workflows/nightly-build.yml" progress(f"Now, start a deep-tests workflow manually for branch {self.release_branch_name} at\n" - f"<{DEEPTESTS_URL}>.\n" - "Once it completes, re-run this script with argument `release`.") + f"<{DEEPTESTS_URL}>\n" + f"To do so, click Run workflow, use workflow from {self.release_branch_name},\n" + f"Once it completes, just re-run this script as `./Scripts/prepare_release.py {self.version} release` to tag the branch and push it to trigger the release.") progress() def _tag_release(self): @@ -429,7 +442,7 @@ def parse_arguments() -> argparse.Namespace: parser.add_argument("--source-branch", help="Which branch to release from (optional, defaults to 'master')", default="master") parser.add_argument("version", help="Version number for this release (A.B.C-xyz)") parser.add_argument("action", help="Which part of the release process to run", - choices=["prepare", "release"]) + choices=["prepare", "release", "set-next-version"]) return parser.parse_args() def main() -> None: @@ -437,6 +450,7 @@ def main() -> None: try: release = (DryRunRelease if args.dry_run else Release)(args.version, args.source_branch) {"prepare": release.prepare, + "set-next-version": release.set_next_version, "release": release.release}[args.action]() except CannotReleaseError: sys.exit(1) diff --git a/Scripts/quicktest.sh b/Scripts/quicktest.sh index ae8053131b4..4909759f8e7 100755 --- a/Scripts/quicktest.sh +++ b/Scripts/quicktest.sh @@ -64,7 +64,7 @@ $DAFNY build -t:go c.dfy | diff - $tmp || exit 1 (cd c-go; GO111MODULE=auto GOPATH=`pwd` go run src/c.go) | diff - $tmpx || exit 1 echo Building with Python $DAFNY build -t:py c.dfy | diff - $tmp || exit 1 -python c-py/c.py | diff - $tmpx || exit 1 +python c-py/__main__.py | diff - $tmpx || exit 1 echo Building with Rust $DAFNY build -t:rs c.dfy | diff - $tmp || exit 1 ./c-rust/target/debug/c | diff - $tmpx || exit 1 diff --git a/Scripts/test-dafny.sh b/Scripts/test-dafny.sh new file mode 100644 index 00000000000..cb684eea9aa --- /dev/null +++ b/Scripts/test-dafny.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Check for required environment variables +if [ -z "$DIR" ]; then + echo "Error: DIR environment variable is not set." + exit 1 +fi + +if [ -z "$name" ]; then + echo "Error: name environment variable is not set." + exit 1 +fi + +# Set the default build flag +NO_BUILD=${NO_BUILD:-false} + +# Locate files matching the specified pattern +files=$(cd "${DIR}/Source/IntegrationTests/TestFiles/LitTests/LitTest" && find . -type f -wholename "*$name*" | grep -E '\.dfy$') + +if [ -z "$files" ]; then + echo "No files found matching pattern: $name" + exit 1 +else + count=$(echo "$files" | wc -l) + echo "$files" + echo "$count test files found." + for file in $files; do + filedir=$(dirname "$file") + ( + cd "${DIR}/Source/IntegrationTests/TestFiles/LitTests/LitTest/$filedir" || exit + + build_flag="" + [ "$NO_BUILD" = "true" ] && build_flag="--no-build" + + dotnet run $build_flag --project "${DIR}/Source/Dafny" -- ${action} "$(basename "$file")" + ) + done +fi \ No newline at end of file diff --git a/Scripts/test.sh b/Scripts/test.sh new file mode 100644 index 00000000000..c094b52c530 --- /dev/null +++ b/Scripts/test.sh @@ -0,0 +1,59 @@ +#!/bin/bash +set -e + +# Ensure name is provided +if [ -z "$name" ]; then + echo "Syntax: make test name= [update=true] [build=false]" + exit 1 +fi + +# Default values for update and build +update_flag=${update:-false} +build_flag=${build:-true} + +# Set the integration tests folder +integration_tests_dir="${DIR}/Source/IntegrationTests" +lit_tests_dir="${integration_tests_dir}/TestFiles/LitTests/LitTest" + +# Handle no-build logic +if [ "$build_flag" = "false" ]; then + echo "" + echo "Build is disabled. Copying test files to the output directory..." + + framework_dir=$(find "${integration_tests_dir}/bin/Debug" -maxdepth 1 -type d -name "net*" | sort | tail -n 1) + if [ -z "$framework_dir" ]; then + echo "Error: Could not find target framework directory in bin/Debug. Please run at least once with build=true." + exit 1 + fi + output_dir="${framework_dir}/TestFiles/LitTests/LitTest" + + # Find and copy all matching files to the output directory + files=$(cd "$lit_tests_dir" && find . -type f -regex '.*\.\(check\|dfy\|expect\)' -wholename "*$name*") + if [ -z "$files" ]; then + echo "No files found matching pattern: $name" + exit 1 + fi + + # Create output directory if it doesn't exist + mkdir -p "$output_dir" + + # Copy files + echo "$files" | while IFS= read -r file; do + echo "Copying $file to $output_dir" + cp "$lit_tests_dir/$file" "$output_dir/$file" + done +fi + +# Check if update flag is true +if [ "$update_flag" = "true" ]; then + echo "" + echo "Update mode enabled." + echo "Going to update the .expect files to match the current Dafny output." +fi + +# Run dotnet test +echo "Running integration tests..." +DAFNY_INTEGRATION_TESTS_UPDATE_EXPECT_FILE="$update_flag" \ +dotnet test "$integration_tests_dir" \ + $( [ "$build_flag" = "false" ] && echo "--no-build" ) \ + --filter "DisplayName~$name" diff --git a/Scripts/use-local-boogie.sh b/Scripts/use-local-boogie.sh index e3e23d2c0b2..49f1f45613d 100755 --- a/Scripts/use-local-boogie.sh +++ b/Scripts/use-local-boogie.sh @@ -2,5 +2,5 @@ BOOGIE_PATH="boogie/" allDlls=("Boogie.AbstractInterpretation.dll" "Boogie.BaseTypes.dll" "Boogie.CodeContractsExtender.dll" "Boogie.Concurrency.dll" "Boogie.Core.dll" "Boogie.ExecutionEngine.dll" "Boogie.Graph.dll" "Boogie.Houdini.dll" "Boogie.Model.dll" "Boogie.Predication.dll" "Boogie.Provers.SMTLib.dll" "Boogie.VCExpr.dll" "Boogie.VCGeneration.dll" "BoogieDriver.dll" "System.Configuration.ConfigurationManager.dll" "System.Runtime.Caching.dll" "System.Security.Cryptography.ProtectedData.dll" "System.Security.Permissions.dll") for t in "${allDlls[@]}" do - cp "${BOOGIE_PATH}Source/BoogieDriver/bin/Release/net6.0/${t}" Binaries/ + cp "${BOOGIE_PATH}Source/BoogieDriver/bin/Release/net8.0/${t}" Binaries/ done diff --git a/Source/AutoExtern.Test/AutoExtern.Test.csproj b/Source/AutoExtern.Test/AutoExtern.Test.csproj index 0c4e1dc0b51..08593d7dba6 100644 --- a/Source/AutoExtern.Test/AutoExtern.Test.csproj +++ b/Source/AutoExtern.Test/AutoExtern.Test.csproj @@ -2,7 +2,6 @@ Exe - net6.0 enable enable false diff --git a/Source/AutoExtern.Test/Minimal/Library.csproj b/Source/AutoExtern.Test/Minimal/Library.csproj index 132c02c59c2..30402ac0e7a 100644 --- a/Source/AutoExtern.Test/Minimal/Library.csproj +++ b/Source/AutoExtern.Test/Minimal/Library.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable diff --git a/Source/AutoExtern.Test/Tutorial/ClientApp/ClientApp.csproj b/Source/AutoExtern.Test/Tutorial/ClientApp/ClientApp.csproj index 86b01ff865f..7ba274e8ce9 100644 --- a/Source/AutoExtern.Test/Tutorial/ClientApp/ClientApp.csproj +++ b/Source/AutoExtern.Test/Tutorial/ClientApp/ClientApp.csproj @@ -2,9 +2,9 @@ Exe - net6.0 + net8.0 enable - CS3021; CS0162 + CS8981; CS3021; CS0162 diff --git a/Source/AutoExtern.Test/Tutorial/ClientApp/GroceryListPrinter.dfy.expect b/Source/AutoExtern.Test/Tutorial/ClientApp/GroceryListPrinter.dfy.expect index bdc3a6c4936..44366de2ee0 100644 --- a/Source/AutoExtern.Test/Tutorial/ClientApp/GroceryListPrinter.dfy.expect +++ b/Source/AutoExtern.Test/Tutorial/ClientApp/GroceryListPrinter.dfy.expect @@ -1,6 +1,8 @@ +Warning: this way of using the CLI is deprecated. Use 'dafny --help' to see help for the new Dafny CLI format Dafny program verifier did not attempt verification Wrote textual form of target program to GroceryListPrinter.cs +Additional output written to GroceryListPrinter-cs.dtr # Printing a grocery list (2 items in the list) ## Apple: 3 @ 1.00 diff --git a/Source/AutoExtern.Test/Tutorial/ClientApp/LibraryModel.dfy.template b/Source/AutoExtern.Test/Tutorial/ClientApp/LibraryModel.dfy.template index b26dde534c6..739861e177a 100644 --- a/Source/AutoExtern.Test/Tutorial/ClientApp/LibraryModel.dfy.template +++ b/Source/AutoExtern.Test/Tutorial/ClientApp/LibraryModel.dfy.template @@ -7,7 +7,7 @@ module {:extern "App"} {:compile false} App {} module {:compile false} {:extern "ExactArithmetic"} App.ExactArithmetic { import opened System type {:compile false} {:extern} Decimal { - function ToStr(): (s: System.String) + function {:axiom} ToStr(): (s: System.String) } } diff --git a/Source/AutoExtern.Test/Tutorial/Library/Library.csproj b/Source/AutoExtern.Test/Tutorial/Library/Library.csproj index 132c02c59c2..30402ac0e7a 100644 --- a/Source/AutoExtern.Test/Tutorial/Library/Library.csproj +++ b/Source/AutoExtern.Test/Tutorial/Library/Library.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable diff --git a/Source/AutoExtern.Test/coverlet.runsettings b/Source/AutoExtern.Test/coverlet.runsettings new file mode 100644 index 00000000000..11c8afc63f8 --- /dev/null +++ b/Source/AutoExtern.Test/coverlet.runsettings @@ -0,0 +1,12 @@ + + + + + + + [*]DAST.*,[*]DCOMP.* + + + + + diff --git a/Source/AutoExtern/AutoExtern.csproj b/Source/AutoExtern/AutoExtern.csproj index 0c00890cffc..f44de5524b4 100644 --- a/Source/AutoExtern/AutoExtern.csproj +++ b/Source/AutoExtern/AutoExtern.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable enable false diff --git a/Source/Dafny.sln b/Source/Dafny.sln index 23297b185ed..91dbe9dc747 100644 --- a/Source/Dafny.sln +++ b/Source/Dafny.sln @@ -43,6 +43,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyDriver.Test", "DafnyDr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyCore.Test", "DafnyCore.Test\DafnyCore.Test.csproj", "{33C29F26-A27B-474D-B436-83EA615B09FC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scripts", "Scripts\Scripts.csproj", "{3FAB051A-1745-497B-B4C0-D49194BB5D32}" +EndProject EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -130,6 +132,10 @@ Global {96B8ADA8-6190-49F7-8C38-CDA60DC92293}.Debug|Any CPU.Build.0 = Debug|Any CPU {96B8ADA8-6190-49F7-8C38-CDA60DC92293}.Release|Any CPU.ActiveCfg = Release|Any CPU {96B8ADA8-6190-49F7-8C38-CDA60DC92293}.Release|Any CPU.Build.0 = Release|Any CPU + {3FAB051A-1745-497B-B4C0-D49194BB5D32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FAB051A-1745-497B-B4C0-D49194BB5D32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FAB051A-1745-497B-B4C0-D49194BB5D32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FAB051A-1745-497B-B4C0-D49194BB5D32}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/Dafny/Dafny.cs b/Source/Dafny/Dafny.cs index 6de73007627..c99ff44e385 100644 --- a/Source/Dafny/Dafny.cs +++ b/Source/Dafny/Dafny.cs @@ -1,9 +1,10 @@ +using System.Threading.Tasks; using Microsoft.Dafny; namespace Dafny; public class Dafny { - public static int Main(string[] args) { - return DafnyDriver.Main(args); + public static Task Main(string[] args) { + return DafnyBackwardsCompatibleCli.Main(args); } } diff --git a/Source/Dafny/Dafny.csproj b/Source/Dafny/Dafny.csproj index 0ac53285657..7c639eb2b7e 100644 --- a/Source/Dafny/Dafny.csproj +++ b/Source/Dafny/Dafny.csproj @@ -5,7 +5,7 @@ Dafny true TRACE - net6.0 + Major ..\..\Binaries\ false @@ -15,7 +15,7 @@ README.md - + false false diff --git a/Source/DafnyBenchmarkingPlugin/BenchmarkInstrumentation.cs b/Source/DafnyBenchmarkingPlugin/BenchmarkInstrumentation.cs index 79daf0fd340..9a8291c09f3 100644 --- a/Source/DafnyBenchmarkingPlugin/BenchmarkInstrumentation.cs +++ b/Source/DafnyBenchmarkingPlugin/BenchmarkInstrumentation.cs @@ -6,12 +6,12 @@ public class BenchmarkingCompilerInstrumenter : CompilerInstrumenter { public BenchmarkingCompilerInstrumenter(ErrorReporter reporter) : base(reporter) { } - public override void Instrument(IExecutableBackend backend, SinglePassCompiler compiler, Program program) { - if (compiler is JavaCompiler javaCompiler) { + public override void Instrument(IExecutableBackend backend, SinglePassCodeGenerator codeGenerator, Program program) { + if (codeGenerator is JavaCodeGenerator javaCompiler) { javaCompiler.AddInstrumenter(new JavaBenchmarkCompilationInstrumenter(Reporter)); } else { - Reporter.Error(MessageSource.Compiler, ResolutionErrors.ErrorId.none, program.GetFirstTopLevelToken(), - $"The benchmarking plugin does not support this compilation target: {compiler} (--target:{backend.TargetId})"); + Reporter.Error(MessageSource.Compiler, ResolutionErrors.ErrorId.none, program.GetStartOfFirstFileToken(), + $"The benchmarking plugin does not support this compilation target: {codeGenerator} (--target:{backend.TargetId})"); } } } \ No newline at end of file diff --git a/Source/DafnyBenchmarkingPlugin/DafnyBenchmarkingPlugin.csproj b/Source/DafnyBenchmarkingPlugin/DafnyBenchmarkingPlugin.csproj index badfad3b141..85823c27a94 100644 --- a/Source/DafnyBenchmarkingPlugin/DafnyBenchmarkingPlugin.csproj +++ b/Source/DafnyBenchmarkingPlugin/DafnyBenchmarkingPlugin.csproj @@ -1,14 +1,20 @@ - net6.0 + Major enable enable ..\..\Binaries\ + MIT + README.md + + + + diff --git a/Source/DafnyBenchmarkingPlugin/JavaBenchmarkCompilationInstrumenter.cs b/Source/DafnyBenchmarkingPlugin/JavaBenchmarkCompilationInstrumenter.cs index 595e22f2e77..1093739a14c 100644 --- a/Source/DafnyBenchmarkingPlugin/JavaBenchmarkCompilationInstrumenter.cs +++ b/Source/DafnyBenchmarkingPlugin/JavaBenchmarkCompilationInstrumenter.cs @@ -21,7 +21,7 @@ public override void BeforeMethod(Method m, ConcreteSyntaxTree wr) { if (Attributes.Contains(m.EnclosingClass.Attributes, "benchmarks")) { if (m is Constructor) { if (m.Ins.Any()) { - Reporter.Error(MessageSource.Compiler, ResolutionErrors.ErrorId.none, m.tok, + Reporter.Error(MessageSource.Compiler, ResolutionErrors.ErrorId.none, m.Origin, $"Classes with {{:benchmarks}} can not accept parameters in their constructors"); } @@ -34,7 +34,7 @@ public override void BeforeMethod(Method m, ConcreteSyntaxTree wr) { wr.WriteLine("@org.openjdk.jmh.annotations.Setup(org.openjdk.jmh.annotations.Level.Iteration)"); } else if (Attributes.Contains(m.Attributes, "benchmarkTearDown")) { if (m.Ins.Any()) { - Reporter.Error(MessageSource.Compiler, ResolutionErrors.ErrorId.none, m.tok, + Reporter.Error(MessageSource.Compiler, ResolutionErrors.ErrorId.none, m.Origin, $"Methods with {{:benchmarkTearDown}} can not accept parameters"); } wr.WriteLine("@org.openjdk.jmh.annotations.TearDown(org.openjdk.jmh.annotations.Level.Iteration)"); diff --git a/Source/DafnyCore.Test/ClonerTest.cs b/Source/DafnyCore.Test/ClonerTest.cs index 5a2b0227154..8d0dccc169a 100644 --- a/Source/DafnyCore.Test/ClonerTest.cs +++ b/Source/DafnyCore.Test/ClonerTest.cs @@ -1,6 +1,7 @@ using Microsoft.Dafny; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; using Tomlyn; namespace DafnyCore.Test; @@ -10,28 +11,31 @@ class DummyDecl : Declaration { public DummyDecl(Cloner cloner, Declaration original) : base(cloner, original) { } - public DummyDecl(RangeToken rangeToken, Name name, Attributes attributes, bool isRefining) : base(rangeToken, name, + public DummyDecl(IOrigin origin, Name name, Attributes attributes, bool isRefining) : base(origin, name, attributes, isRefining) { } + + public override SymbolKind? Kind => null; + public override string GetDescription(DafnyOptions options) { + return ""; + } } [Fact] public void ClonerKeepsBodyStartTok() { var tokenBodyStart = new Token() { line = 2 }; - var rangeToken = new RangeToken(Token.NoToken, Token.NoToken); + var rangeToken = new SourceOrigin(Token.NoToken, Token.NoToken); var specificationFrame = new LiteralExpr(Microsoft.Dafny.Token.NoToken, 1); - var formal1 = new Formal(Token.NoToken, "a", Microsoft.Dafny.Type.Bool, true, false, null) { - RangeToken = new RangeToken(tokenBodyStart, tokenBodyStart), + var formal1 = new Formal(new SourceOrigin(tokenBodyStart, tokenBodyStart), "a", Microsoft.Dafny.Type.Bool, true, false, null) { IsTypeExplicit = true }; - var formal2 = new Formal(Token.NoToken, "b", Microsoft.Dafny.Type.Bool, true, false, null) { - RangeToken = new RangeToken(tokenBodyStart, tokenBodyStart), + var formal2 = new Formal(new SourceOrigin(tokenBodyStart, tokenBodyStart), "b", Microsoft.Dafny.Type.Bool, true, false, null) { IsTypeExplicit = false }; var dummyDecl = new Method(rangeToken, new Name(rangeToken, "hello"), false, false, new List(), new List { formal1, formal2 }, new List(), new List(), - new Specification(new List(), null), + new Specification(), new Specification(new List(), null), new List(), new Specification(new List(), null), new BlockStmt(rangeToken, new List()), null, Token.NoToken, false); @@ -39,9 +43,9 @@ public void ClonerKeepsBodyStartTok() { var cloner = new Cloner(); var dummyDecl2 = cloner.CloneMethod(dummyDecl); Assert.Equal(2, dummyDecl2.BodyStartTok.line); - Assert.Equal(2, dummyDecl2.Ins[0].RangeToken.StartToken.line); + Assert.Equal(2, dummyDecl2.Ins[0].Origin.StartToken.line); Assert.True(dummyDecl2.Ins[0].IsTypeExplicit); - Assert.Equal(2, dummyDecl2.Ins[1].RangeToken.StartToken.line); + Assert.Equal(2, dummyDecl2.Ins[1].Origin.StartToken.line); Assert.False(dummyDecl2.Ins[1].IsTypeExplicit); } } \ No newline at end of file diff --git a/Source/DafnyCore.Test/DafnyCore.Test.csproj b/Source/DafnyCore.Test/DafnyCore.Test.csproj index 8e9d3e8aeba..e585e29faf8 100644 --- a/Source/DafnyCore.Test/DafnyCore.Test.csproj +++ b/Source/DafnyCore.Test/DafnyCore.Test.csproj @@ -1,7 +1,6 @@ - net6.0 enable enable diff --git a/Source/DafnyCore.Test/DafnyProjectTest.cs b/Source/DafnyCore.Test/DafnyProjectTest.cs index 836b21309bc..ca68a2ce67d 100644 --- a/Source/DafnyCore.Test/DafnyProjectTest.cs +++ b/Source/DafnyCore.Test/DafnyProjectTest.cs @@ -1,40 +1,37 @@ using Microsoft.Dafny; -namespace DafnyCore.Test; +namespace DafnyCore.Test; public class DafnyProjectTest { [Fact] public void Equality() { var randomFileName = Path.GetTempFileName(); - var first = new DafnyProject() { - Uri = new Uri(randomFileName, UriKind.Absolute), - Includes = new[] { "a", "a2" }, - Excludes = new[] { "b", "b2" }, - Options = new Dictionary() { + var first = new DafnyProject(null, new Uri(randomFileName, UriKind.Absolute), null, + new[] { "a", "a2" }.ToHashSet(), + new[] { "b", "b2" }.ToHashSet(), new Dictionary() { { "c", "d" }, { "e", "f" } } - }; + ); - var second = new DafnyProject() { - Uri = new Uri(randomFileName, UriKind.Absolute), - Includes = new[] { "a2", "a" }, - Excludes = new[] { "b2", "b" }, - Options = new Dictionary() { + var second = new DafnyProject(null, new Uri(randomFileName, UriKind.Absolute), null, + new[] { "a2", "a" }.ToHashSet(), + new[] { "b2", "b" }.ToHashSet(), + new Dictionary() { { "e", "f" }, { "c", "d" }, } - }; + ); Assert.Equal(first, second); - first.Options.Add("k", new[] { 1, 2, 3 }); - second.Options.Add("k", new[] { 1, 2, 3 }); + first.Options.Add("k", "1, 2, 3"); + second.Options.Add("k", "1, 2, 3"); Assert.Equal(first, second); - first.Options.Add("m", new[] { 1, 2, 3 }); - second.Options.Add("m", new[] { 3, 2, 1 }); + first.Options.Add("m", "1, 2, 3"); + second.Options.Add("m", "3, 2, 1"); Assert.NotEqual(first, second); } } \ No newline at end of file diff --git a/Source/DafnyCore.Test/DooFileTest.cs b/Source/DafnyCore.Test/DooFileTest.cs index 98705c6a52b..93e668fac27 100644 --- a/Source/DafnyCore.Test/DooFileTest.cs +++ b/Source/DafnyCore.Test/DooFileTest.cs @@ -7,15 +7,16 @@ namespace DafnyCore.Test; public class DooFileTest { [Fact] - public void RoundTripCurrentVersion() { + public async Task RoundTripCurrentVersion() { var options = DafnyOptions.Default; options.ApplyDefaultOptionsWithoutSettingsDefault(); - var program = ParseProgram("module MyModule { function TheAnswer(): int { 42 } }", options); + var program = await ParseProgram("module MyModule { function TheAnswer(): int { 42 } }", options); + options.ProcessSolverOptions(program.Reporter, Token.Cli); var dooFile = new DooFile(program); var path = Path.GetTempFileName(); dooFile.Write(path); - var loadedDooFile = DooFile.Read(path); + var loadedDooFile = await DooFile.Read(path); Assert.Equal(loadedDooFile.Manifest.DooFileVersion, DooFile.ManifestData.CurrentDooFileVersion); Assert.Equal(loadedDooFile.Manifest.DafnyVersion, options.VersionNumber); @@ -30,13 +31,13 @@ public void UnknownManifestEntries() { Assert.Throws(() => DooFile.ManifestData.Read(new StringReader(source))); } - private static Program ParseProgram(string dafnyProgramText, DafnyOptions options) { + private static async Task ParseProgram(string dafnyProgramText, DafnyOptions options) { const string fullFilePath = "untitled:foo"; var rootUri = new Uri(fullFilePath); Microsoft.Dafny.Type.ResetScopes(); var errorReporter = new ConsoleErrorReporter(options); - var program = new ProgramParser().Parse(dafnyProgramText, rootUri, errorReporter); + var parseResult = await new ProgramParser().Parse(dafnyProgramText, rootUri, errorReporter); Assert.Equal(0, errorReporter.ErrorCount); - return program; + return parseResult.Program; } } diff --git a/Source/DafnyCore.Test/GeneratedDafnyTest.cs b/Source/DafnyCore.Test/GeneratedDafnyTest.cs new file mode 100644 index 00000000000..3d4595d4e3f --- /dev/null +++ b/Source/DafnyCore.Test/GeneratedDafnyTest.cs @@ -0,0 +1,26 @@ +using System.Collections.Concurrent; +using Microsoft.Dafny; + +namespace DafnyCore.Test; + +public class GeneratedDafnyTest { + [Fact] + public void TestDafnyCoverage() { + DafnyToRustCompilerCoverage.__default.TestIsCopy(); ; + } + + [Fact] + public void TestRustASTCoverage() { + RASTCoverage.__default.TestExpr(); + } + + [Fact] + public void TestPathSimplification() { + FactorPathsOptimizationTest.__default.TestApply(); + } + + [Fact] + public void TestDefsCoverage() { + DefsCoverage.__default.Tests(); + } +} \ No newline at end of file diff --git a/Source/DafnyCore.Test/GeneratedFromDafny/DafnyToRustCompilerCoverage.cs b/Source/DafnyCore.Test/GeneratedFromDafny/DafnyToRustCompilerCoverage.cs new file mode 100644 index 00000000000..699aeffaaf2 --- /dev/null +++ b/Source/DafnyCore.Test/GeneratedFromDafny/DafnyToRustCompilerCoverage.cs @@ -0,0 +1,32 @@ +// Dafny program the_program compiled into C# +// To recompile, you will need the libraries +// System.Runtime.Numerics.dll System.Collections.Immutable.dll +// but the 'dotnet' tool in .NET should pick those up automatically. +// Optionally, you may want to include compiler switches like +// /debug /nowarn:162,164,168,183,219,436,1717,1718 + +using System; +using System.Numerics; +using System.Collections; +#pragma warning disable CS0164 // This label has not been referenced +#pragma warning disable CS0162 // Unreachable code detected +#pragma warning disable CS1717 // Assignment made to same variable + +namespace DafnyToRustCompilerCoverage { + + public partial class __default { + public static void TestIsCopy() + { + if (!(Defs.__default.IsNewtypeCopy(DAST.NewtypeRange.create_Bool()))) { + throw new Dafny.HaltException("Backends/Rust/Dafny-compiler-rust-coverage.dfy(9,4): " + Dafny.Sequence.UnicodeFromString("expectation violation").ToVerbatimString(false));} + if (!(Defs.__default.IsNewtypeCopy(DAST.NewtypeRange.create_U128(true)))) { + throw new Dafny.HaltException("Backends/Rust/Dafny-compiler-rust-coverage.dfy(10,4): " + Dafny.Sequence.UnicodeFromString("expectation violation").ToVerbatimString(false));} + if (!(Defs.__default.IsNewtypeCopy(DAST.NewtypeRange.create_U128(false)))) { + throw new Dafny.HaltException("Backends/Rust/Dafny-compiler-rust-coverage.dfy(11,4): " + Dafny.Sequence.UnicodeFromString("expectation violation").ToVerbatimString(false));} + if (!(!(Defs.__default.IsNewtypeCopy(DAST.NewtypeRange.create_BigInt())))) { + throw new Dafny.HaltException("Backends/Rust/Dafny-compiler-rust-coverage.dfy(12,4): " + Dafny.Sequence.UnicodeFromString("expectation violation").ToVerbatimString(false));} + if (!(!(Defs.__default.IsNewtypeCopy(DAST.NewtypeRange.create_NoRange())))) { + throw new Dafny.HaltException("Backends/Rust/Dafny-compiler-rust-coverage.dfy(13,4): " + Dafny.Sequence.UnicodeFromString("expectation violation").ToVerbatimString(false));} + } + } +} // end of namespace DafnyToRustCompilerCoverage \ No newline at end of file diff --git a/Source/DafnyCore.Test/GeneratedFromDafny/DefsCoverage.cs b/Source/DafnyCore.Test/GeneratedFromDafny/DefsCoverage.cs new file mode 100644 index 00000000000..c31e0447b04 --- /dev/null +++ b/Source/DafnyCore.Test/GeneratedFromDafny/DefsCoverage.cs @@ -0,0 +1,83 @@ +// Dafny program the_program compiled into C# +// To recompile, you will need the libraries +// System.Runtime.Numerics.dll System.Collections.Immutable.dll +// but the 'dotnet' tool in .NET should pick those up automatically. +// Optionally, you may want to include compiler switches like +// /debug /nowarn:162,164,168,183,219,436,1717,1718 + +using System; +using System.Numerics; +using System.Collections; +#pragma warning disable CS0164 // This label has not been referenced +#pragma warning disable CS0162 // Unreachable code detected +#pragma warning disable CS1717 // Assignment made to same variable + +namespace DefsCoverage { + + public partial class __default { + public static void Expect(bool x) + { + if (!(x)) { + throw new Dafny.HaltException("Backends/Rust/Dafny-compiler-rust-definitions-coverage.dfy(17,4): " + Dafny.Sequence.UnicodeFromString("expectation violation").ToVerbatimString(false));} + } + public static void Tests() + { + DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Join(Defs.AssignmentStatus.create_SurelyAssigned()), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Join(Defs.AssignmentStatus.create_NotAssigned()), Defs.AssignmentStatus.create_NotAssigned())); + DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Join(Defs.AssignmentStatus.create_SurelyAssigned()), Defs.AssignmentStatus.create_Unknown())); + DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Join(Defs.AssignmentStatus.create_NotAssigned()), Defs.AssignmentStatus.create_Unknown())); + DefsCoverage.__default.Expect((object.Equals((Defs.AssignmentStatus.create_Unknown()).Join(Defs.AssignmentStatus.create_NotAssigned()), (Defs.AssignmentStatus.create_NotAssigned()).Join(Defs.AssignmentStatus.create_Unknown()))) && (object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Join(Defs.AssignmentStatus.create_Unknown()), Defs.AssignmentStatus.create_Unknown()))); + DefsCoverage.__default.Expect((object.Equals((Defs.AssignmentStatus.create_Unknown()).Join(Defs.AssignmentStatus.create_SurelyAssigned()), (Defs.AssignmentStatus.create_SurelyAssigned()).Join(Defs.AssignmentStatus.create_Unknown()))) && (object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Join(Defs.AssignmentStatus.create_Unknown()), Defs.AssignmentStatus.create_Unknown()))); + DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_Unknown()).Join(Defs.AssignmentStatus.create_Unknown()), Defs.AssignmentStatus.create_Unknown())); + DefsCoverage.__default.Expect((((object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_Unknown()), (Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_NotAssigned()))) && (object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_NotAssigned()), (Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_SurelyAssigned())))) && (object.Equals((Defs.AssignmentStatus.create_SurelyAssigned()).Then(Defs.AssignmentStatus.create_SurelyAssigned()), (Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_SurelyAssigned())))) && (object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_SurelyAssigned()), Defs.AssignmentStatus.create_SurelyAssigned()))); + DefsCoverage.__default.Expect((((object.Equals((Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_NotAssigned()), (Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_SurelyAssigned()))) && (object.Equals((Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_SurelyAssigned()), (Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_Unknown())))) && (object.Equals((Defs.AssignmentStatus.create_Unknown()).Then(Defs.AssignmentStatus.create_Unknown()), (Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_Unknown())))) && (object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_Unknown()), Defs.AssignmentStatus.create_Unknown()))); + DefsCoverage.__default.Expect(object.Equals((Defs.AssignmentStatus.create_NotAssigned()).Then(Defs.AssignmentStatus.create_NotAssigned()), Defs.AssignmentStatus.create_NotAssigned())); + Dafny.ISequence _0_x; + _0_x = Dafny.Sequence.UnicodeFromString("x"); + Dafny.ISequence _1_y; + _1_y = Dafny.Sequence.UnicodeFromString("y"); + DAST._IExpression _2_z; + _2_z = DAST.Expression.create_Ident(Dafny.Sequence.UnicodeFromString("z")); + Dafny.ISequence _3_assigns__x; + _3_assigns__x = Dafny.Sequence.FromElements(DAST.Statement.create_Assign(DAST.AssignLhs.create_Ident(_0_x), DAST.Expression.create_Ident(_1_y))); + Dafny.ISequence _4_assigns__y; + _4_assigns__y = Dafny.Sequence.FromElements(DAST.Statement.create_Assign(DAST.AssignLhs.create_Ident(_1_y), DAST.Expression.create_Ident(_0_x))); + DAST._IExpression _5_cond; + _5_cond = DAST.Expression.create_Ident(Dafny.Sequence.UnicodeFromString("cond")); + Dafny.ISequence _6_unknown__x; + _6_unknown__x = Dafny.Sequence.FromElements(DAST.Statement.create_If(_5_cond, _3_assigns__x, _4_assigns__y)); + Dafny.ISequence _7_surely__double__x; + _7_surely__double__x = Dafny.Sequence.FromElements(DAST.Statement.create_If(_5_cond, _3_assigns__x, _3_assigns__x)); + Dafny.ISequence _8_call__to__x; + _8_call__to__x = Dafny.Sequence.FromElements(DAST.Statement.create_Call(_2_z, DAST.CallName.create_SetBuilderAdd(), Dafny.Sequence.FromElements(), Dafny.Sequence.FromElements(), Std.Wrappers.Option>>.create_Some(Dafny.Sequence>.FromElements(_0_x)))); + Dafny.ISequence _9_declare__x__again; + _9_declare__x__again = Dafny.Sequence.FromElements(DAST.Statement.create_DeclareVar(_0_x, DAST.Type.create_Tuple(Dafny.Sequence.FromElements()), Std.Wrappers.Option.create_None())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_4_assigns__y, _0_x), Defs.AssignmentStatus.create_NotAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_3_assigns__x, _0_x), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_3_assigns__x, _1_y), Defs.AssignmentStatus.create_NotAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(_3_assigns__x, _4_assigns__y), _1_y), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_6_unknown__x, _0_x), Defs.AssignmentStatus.create_Unknown())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_7_surely__double__x, _0_x), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_7_surely__double__x, _1_y), Defs.AssignmentStatus.create_NotAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_8_call__to__x, _0_x), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(_8_call__to__x, _1_y), Defs.AssignmentStatus.create_NotAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(_8_call__to__x, _4_assigns__y), _1_y), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_Labeled(Dafny.Sequence.UnicodeFromString("l"), _4_assigns__y)), _3_assigns__x), _1_y), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_Labeled(Dafny.Sequence.UnicodeFromString("l"), _3_assigns__x)), _4_assigns__y), _0_x), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_Labeled(Dafny.Sequence.UnicodeFromString("l"), _3_assigns__x)), _4_assigns__y), _0_x), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(_9_declare__x__again, _3_assigns__x), _0_x), Defs.AssignmentStatus.create_NotAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(_9_declare__x__again, _4_assigns__y), _1_y), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_Return(_2_z)), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_NotAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_EarlyReturn()), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_NotAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_JumpTailCallStart()), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_NotAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_Print(_2_z)), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_Print(_2_z)), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_SurelyAssigned())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_While(_2_z, Dafny.Sequence.FromElements())), _3_assigns__x), _0_x), Defs.AssignmentStatus.create_Unknown())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.FromElements(DAST.Statement.create_If(_5_cond, _3_assigns__x, Dafny.Sequence.FromElements(DAST.Statement.create_If(_5_cond, _3_assigns__x, _4_assigns__y)))), _0_x), Defs.AssignmentStatus.create_Unknown())); + DefsCoverage.__default.Expect(object.Equals(Defs.__default.DetectAssignmentStatus(Dafny.Sequence.FromElements(DAST.Statement.create_If(_5_cond, Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_If(_5_cond, Dafny.Sequence.FromElements(DAST.Statement.create_Return(_2_z)), Dafny.Sequence.FromElements())), _3_assigns__x), Dafny.Sequence.FromElements(DAST.Statement.create_If(_5_cond, Dafny.Sequence.Concat(Dafny.Sequence.FromElements(DAST.Statement.create_If(_5_cond, Dafny.Sequence.FromElements(DAST.Statement.create_Return(_2_z)), Dafny.Sequence.FromElements())), _3_assigns__x), _4_assigns__y)))), _0_x), Defs.AssignmentStatus.create_Unknown())); + } + public static Dafny.ISequence IND { get { + return RAST.__default.IND; + } } + } +} // end of namespace DefsCoverage \ No newline at end of file diff --git a/Source/DafnyCore.Test/GeneratedFromDafny/FactorPathsOptimizationTest.cs b/Source/DafnyCore.Test/GeneratedFromDafny/FactorPathsOptimizationTest.cs new file mode 100644 index 00000000000..04573118e97 --- /dev/null +++ b/Source/DafnyCore.Test/GeneratedFromDafny/FactorPathsOptimizationTest.cs @@ -0,0 +1,49 @@ +// Dafny program the_program compiled into C# +// To recompile, you will need the libraries +// System.Runtime.Numerics.dll System.Collections.Immutable.dll +// but the 'dotnet' tool in .NET should pick those up automatically. +// Optionally, you may want to include compiler switches like +// /debug /nowarn:162,164,168,183,219,436,1717,1718 + +using System; +using System.Numerics; +using System.Collections; +#pragma warning disable CS0164 // This label has not been referenced +#pragma warning disable CS0162 // Unreachable code detected +#pragma warning disable CS1717 // Assignment made to same variable + +namespace FactorPathsOptimizationTest { + + public partial class __default { + public static void ShouldBeEqual(RAST._IMod a, RAST._IMod b) + { + Dafny.ISequence _0_sA; + _0_sA = (a)._ToString(Dafny.Sequence.UnicodeFromString("")); + Dafny.ISequence _1_sB; + _1_sB = (b)._ToString(Dafny.Sequence.UnicodeFromString("")); + if (!(_0_sA).Equals(_1_sB)) { + Dafny.Helpers.Print((Dafny.Sequence.Concat(Dafny.Sequence.Concat(Dafny.Sequence.UnicodeFromString("Got:\n"), _0_sA), Dafny.Sequence.UnicodeFromString("\n"))).ToVerbatimString(false)); + Dafny.Helpers.Print((Dafny.Sequence.Concat(Dafny.Sequence.Concat(Dafny.Sequence.UnicodeFromString("Expected:\n"), _1_sB), Dafny.Sequence.UnicodeFromString("\n"))).ToVerbatimString(false)); + if (!((_0_sA).Equals(_1_sB))) { + throw new Dafny.HaltException("Backends/Rust/Dafny-compiler-rust-path-simplification.dfy(13,6): " + Dafny.Sequence.UnicodeFromString("expectation violation").ToVerbatimString(false));} + } + } + public static void TestApply() + { + RAST._ITypeParamDecl _0_T__Decl; + _0_T__Decl = RAST.TypeParamDecl.create(Dafny.Sequence.UnicodeFromString("T"), Dafny.Sequence.FromElements(RAST.__default.DafnyType)); + RAST._ITypeParamDecl _1_T__Decl__simp; + _1_T__Decl__simp = RAST.TypeParamDecl.create(Dafny.Sequence.UnicodeFromString("T"), Dafny.Sequence.FromElements(RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("DafnyType")))); + RAST._IType _2_T; + _2_T = RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("T")); + RAST._IPath _3_std__any__Any; + _3_std__any__Any = (((RAST.__default.@global).MSel(Dafny.Sequence.UnicodeFromString("std"))).MSel(Dafny.Sequence.UnicodeFromString("any"))).MSel(Dafny.Sequence.UnicodeFromString("Any")); + RAST._IType _4_Any; + _4_Any = RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("Any")); + FactorPathsOptimizationTest.__default.ShouldBeEqual(Dafny.Helpers.Id>(FactorPathsOptimization.__default.apply(RAST.__default.crate))(RAST.Mod.create_Mod(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("onemodule"), Dafny.Sequence.FromElements(RAST.ModDecl.create_StructDecl(RAST.Struct.create(RAST.__default.NoDoc, Dafny.Sequence>.FromElements(), Dafny.Sequence.UnicodeFromString("test"), Dafny.Sequence.FromElements(_0_T__Decl), RAST.Fields.create_NamedFields(Dafny.Sequence.FromElements(RAST.Field.create(RAST.Visibility.create_PUB(), RAST.Formal.create(Dafny.Sequence.UnicodeFromString("a"), (_3_std__any__Any).AsType())))))), RAST.ModDecl.create_ImplDecl(RAST.Impl.create_Impl(Dafny.Sequence.FromElements(_0_T__Decl), (RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("test"))).Apply(Dafny.Sequence.FromElements(_2_T)), Dafny.Sequence.FromElements())), RAST.ModDecl.create_ImplDecl(RAST.Impl.create_ImplFor(Dafny.Sequence.FromElements(_0_T__Decl), (_3_std__any__Any).AsType(), ((((RAST.__default.crate).MSel(Dafny.Sequence.UnicodeFromString("onemodule"))).MSel(Dafny.Sequence.UnicodeFromString("test"))).AsType()).Apply(Dafny.Sequence.FromElements(_2_T)), Dafny.Sequence.FromElements()))))), RAST.Mod.create_Mod(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("onemodule"), Dafny.Sequence.FromElements(RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), (RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("DafnyType")))), RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), _3_std__any__Any)), RAST.ModDecl.create_StructDecl(RAST.Struct.create(RAST.__default.NoDoc, Dafny.Sequence>.FromElements(), Dafny.Sequence.UnicodeFromString("test"), Dafny.Sequence.FromElements(_1_T__Decl__simp), RAST.Fields.create_NamedFields(Dafny.Sequence.FromElements(RAST.Field.create(RAST.Visibility.create_PUB(), RAST.Formal.create(Dafny.Sequence.UnicodeFromString("a"), _4_Any)))))), RAST.ModDecl.create_ImplDecl(RAST.Impl.create_Impl(Dafny.Sequence.FromElements(_1_T__Decl__simp), (RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("test"))).Apply(Dafny.Sequence.FromElements(_2_T)), Dafny.Sequence.FromElements())), RAST.ModDecl.create_ImplDecl(RAST.Impl.create_ImplFor(Dafny.Sequence.FromElements(_1_T__Decl__simp), _4_Any, (RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("test"))).Apply(Dafny.Sequence.FromElements(_2_T)), Dafny.Sequence.FromElements()))))); + FactorPathsOptimizationTest.__default.ShouldBeEqual(Dafny.Helpers.Id>(FactorPathsOptimization.__default.apply(RAST.__default.crate))(RAST.Mod.create_Mod(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("onemodule"), Dafny.Sequence.FromElements(RAST.ModDecl.create_ImplDecl(RAST.Impl.create_ImplFor(Dafny.Sequence.FromElements(_0_T__Decl), (((RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("UpcastObject"))).AsType()).Apply(Dafny.Sequence.FromElements(RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("x")))), (RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("test"))).Apply(Dafny.Sequence.FromElements(_2_T)), Dafny.Sequence.FromElements()))))), RAST.Mod.create_Mod(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("onemodule"), Dafny.Sequence.FromElements(RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), (RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("DafnyType")))), RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), (RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("UpcastObject")))), RAST.ModDecl.create_ImplDecl(RAST.Impl.create_ImplFor(Dafny.Sequence.FromElements(_1_T__Decl__simp), (RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("UpcastObject"))).Apply(Dafny.Sequence.FromElements(RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("x")))), (RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("test"))).Apply(Dafny.Sequence.FromElements(_2_T)), Dafny.Sequence.FromElements()))))); + FactorPathsOptimizationTest.__default.ShouldBeEqual(Dafny.Helpers.Id>(FactorPathsOptimization.__default.apply(RAST.__default.crate))(RAST.Mod.create_Mod(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("onemodule"), Dafny.Sequence.FromElements(RAST.ModDecl.create_ConstDecl(RAST.Constant.create(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("dummy"), (_3_std__any__Any).AsType(), RAST.Expr.create_StmtExpr(RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("doit"), Std.Wrappers.Option.create_Some(((RAST.__default.std__rc__Rc).AsType()).Apply1(RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("unknown")))), Std.Wrappers.Option.create_Some(((RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("something"))).ApplyType(Dafny.Sequence.FromElements(RAST.Type.create_DynType((RAST.__default.std__default__Default).AsType())))).Apply(Dafny.Sequence.FromElements(RAST.__default.std__default__Default__default, (((RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("rd!"))).AsExpr()).Apply1(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("obj"))))))), RAST.Expr.create_TypeAscription(RAST.Expr.create_ExprFromType(((RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("DafnyString"))).AsType()), ((RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("DafnyType"))).AsType()))))))), RAST.Mod.create_Mod(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("onemodule"), Dafny.Sequence.FromElements(RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), _3_std__any__Any)), RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), RAST.__default.std__rc__Rc)), RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), RAST.__default.std__default__Default)), RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), (RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("rd")))), RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), (RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("DafnyString")))), RAST.ModDecl.create_UseDecl(RAST.Use.create(RAST.Visibility.create_PUB(), (RAST.__default.dafny__runtime).MSel(Dafny.Sequence.UnicodeFromString("DafnyType")))), RAST.ModDecl.create_ConstDecl(RAST.Constant.create(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("dummy"), RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("Any")), RAST.Expr.create_StmtExpr(RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("doit"), Std.Wrappers.Option.create_Some((RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("Rc"))).Apply1(RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("unknown")))), Std.Wrappers.Option.create_Some(((RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("something"))).ApplyType(Dafny.Sequence.FromElements(RAST.Type.create_DynType(RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("Default")))))).Apply(Dafny.Sequence.FromElements(((RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("Default"))).FSel(Dafny.Sequence.UnicodeFromString("default"))).Apply(Dafny.Sequence.FromElements()), (RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("rd!"))).Apply1(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("obj"))))))), RAST.Expr.create_TypeAscription(RAST.Expr.create_ExprFromType(RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("DafnyString"))), RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("DafnyType"))))))))); + FactorPathsOptimizationTest.__default.ShouldBeEqual(Dafny.Helpers.Id>(FactorPathsOptimization.__default.apply(RAST.__default.crate))(RAST.Mod.create_Mod(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("onemodule"), Dafny.Sequence.FromElements(RAST.ModDecl.create_TraitDecl(RAST.Trait.create(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.FromElements(), RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("Something")), Dafny.Sequence.FromElements(), Dafny.Sequence.FromElements())), RAST.ModDecl.create_ConstDecl(RAST.Constant.create(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("dummyExtern"), (((RAST.__default.crate).MSel(Dafny.Sequence.UnicodeFromString("anothermodule"))).MSel(Dafny.Sequence.UnicodeFromString("Something"))).AsType(), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("nothing")))), RAST.ModDecl.create_ConstDecl(RAST.Constant.create(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("dummyIntern"), (((RAST.__default.crate).MSel(Dafny.Sequence.UnicodeFromString("onemodule"))).MSel(Dafny.Sequence.UnicodeFromString("Something"))).AsType(), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("nothing"))))))), RAST.Mod.create_Mod(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("onemodule"), Dafny.Sequence.FromElements(RAST.ModDecl.create_TraitDecl(RAST.Trait.create(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.FromElements(), RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("Something")), Dafny.Sequence.FromElements(), Dafny.Sequence.FromElements())), RAST.ModDecl.create_ConstDecl(RAST.Constant.create(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("dummyExtern"), (((RAST.__default.crate).MSel(Dafny.Sequence.UnicodeFromString("anothermodule"))).MSel(Dafny.Sequence.UnicodeFromString("Something"))).AsType(), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("nothing")))), RAST.ModDecl.create_ConstDecl(RAST.Constant.create(RAST.__default.NoDoc, RAST.__default.NoAttr, Dafny.Sequence.UnicodeFromString("dummyIntern"), RAST.Type.create_TIdentifier(Dafny.Sequence.UnicodeFromString("Something")), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("nothing"))))))); + } + } +} // end of namespace FactorPathsOptimizationTest \ No newline at end of file diff --git a/Source/DafnyCore.Test/GeneratedFromDafny/RASTCoverage.cs b/Source/DafnyCore.Test/GeneratedFromDafny/RASTCoverage.cs new file mode 100644 index 00000000000..bebebfe2763 --- /dev/null +++ b/Source/DafnyCore.Test/GeneratedFromDafny/RASTCoverage.cs @@ -0,0 +1,165 @@ +// Dafny program the_program compiled into C# +// To recompile, you will need the libraries +// System.Runtime.Numerics.dll System.Collections.Immutable.dll +// but the 'dotnet' tool in .NET should pick those up automatically. +// Optionally, you may want to include compiler switches like +// /debug /nowarn:162,164,168,183,219,436,1717,1718 + +using System; +using System.Numerics; +using System.Collections; +#pragma warning disable CS0164 // This label has not been referenced +#pragma warning disable CS0162 // Unreachable code detected +#pragma warning disable CS1717 // Assignment made to same variable + +namespace RASTCoverage { + + public partial class __default { + public static void AssertCoverage(bool x) + { + if (!(x)) { + throw new Dafny.HaltException("Backends/Rust/Dafny-compiler-rust-coverage.dfy(26,4): " + Dafny.Sequence.UnicodeFromString("expectation violation").ToVerbatimString(false));} + } + public static void TestExpr() + { + RASTCoverage.__default.TestOptimizeToString(); + RASTCoverage.__default.TestPrintingInfo(); + RASTCoverage.__default.TestNoExtraSemicolonAfter(); + } + public static void TestNoOptimize(RAST._IExpr e) + { + } + public static RAST._IExpr ConversionNum(RAST._IType t, RAST._IExpr x) + { + return RAST.Expr.create_Call(RAST.Expr.create_ExprFromPath(RAST.Path.create_PMemberSelect(RAST.Path.create_PMemberSelect(RAST.Path.create_Global(), Dafny.Sequence.UnicodeFromString("dafny_runtime")), Dafny.Sequence.UnicodeFromString("truncate!"))), Dafny.Sequence.FromElements(x, RAST.Expr.create_ExprFromType(t))); + } + public static RAST._IExpr DafnyIntLiteral(Dafny.ISequence s) { + return RAST.Expr.create_Call(RAST.Expr.create_ExprFromPath(RAST.Path.create_PMemberSelect(RAST.__default.dafny__runtime, Dafny.Sequence.UnicodeFromString("int!"))), Dafny.Sequence.FromElements(RAST.Expr.create_LiteralInt(Dafny.Sequence.UnicodeFromString("1")))); + } + public static RAST._IExpr Optimize(RAST._IExpr e) { + return (ExpressionOptimization.__default.ExprSimplifier()).ReplaceExpr(e); + } + public static void TestOptimizeToString() + { + RAST._IExpr _0_x; + _0_x = RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")); + RAST._IExpr _1_y; + _1_y = RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("y")); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("&"), RAST.Expr.create_Call(RAST.Expr.create_Select(_0_x, Dafny.Sequence.UnicodeFromString("clone")), Dafny.Sequence.FromElements(_1_y)), DAST.Format.UnaryOpFormat.create_NoFormat())); + RASTCoverage.__default.AssertCoverage(object.Equals(RASTCoverage.__default.Optimize(RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("!"), RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("=="), _0_x, _1_y, DAST.Format.BinaryOpFormat.create_NoFormat()), DAST.Format.UnaryOpFormat.create_CombineFormat())), RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("!="), _0_x, _1_y, DAST.Format.BinaryOpFormat.create_NoFormat()))); + RASTCoverage.__default.AssertCoverage(object.Equals(RASTCoverage.__default.Optimize(RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("!"), RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("<"), _0_x, _1_y, DAST.Format.BinaryOpFormat.create_NoFormat()), DAST.Format.UnaryOpFormat.create_CombineFormat())), RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString(">="), _0_x, _1_y, DAST.Format.BinaryOpFormat.create_NoFormat()))); + RASTCoverage.__default.AssertCoverage(object.Equals(RASTCoverage.__default.Optimize(RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("!"), RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("<"), _0_x, _1_y, DAST.Format.BinaryOpFormat.create_ReverseFormat()), DAST.Format.UnaryOpFormat.create_CombineFormat())), RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("<="), _1_y, _0_x, DAST.Format.BinaryOpFormat.create_NoFormat()))); + RASTCoverage.__default.AssertCoverage(object.Equals(RASTCoverage.__default.Optimize(RASTCoverage.__default.ConversionNum(RAST.Type.create_I128(), RASTCoverage.__default.DafnyIntLiteral(Dafny.Sequence.UnicodeFromString("1")))), RAST.Expr.create_LiteralInt(Dafny.Sequence.UnicodeFromString("1")))); + RASTCoverage.__default.TestNoOptimize(RASTCoverage.__default.ConversionNum(RAST.Type.create_I128(), _0_x)); + RASTCoverage.__default.AssertCoverage(object.Equals(RASTCoverage.__default.Optimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("z"), Std.Wrappers.Option.create_Some(RAST.Type.create_I128()), Std.Wrappers.Option.create_None()), RAST.Expr.create_StmtExpr(RAST.__default.AssignVar(Dafny.Sequence.UnicodeFromString("z"), _1_y), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("return"))))), RAST.Expr.create_StmtExpr(RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("z"), Std.Wrappers.Option.create_Some(RAST.Type.create_I128()), Std.Wrappers.Option.create_Some(_1_y)), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("return"))))); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("z"), Std.Wrappers.Option.create_Some(RAST.Type.create_I128()), Std.Wrappers.Option.create_None()), RAST.Expr.create_StmtExpr(RAST.__default.AssignVar(Dafny.Sequence.UnicodeFromString("w"), _1_y), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("return"))))); + RASTCoverage.__default.TestNoOptimize(_0_x); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(_0_x, _0_x)); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_Match(_0_x, Dafny.Sequence.FromElements()), _0_x)); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_StructBuild(_0_x, Dafny.Sequence.FromElements()), _0_x)); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_Tuple(Dafny.Sequence.FromElements()), _0_x)); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("&"), _0_x, DAST.Format.UnaryOpFormat.create_NoFormat()), _0_x)); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("&&"), _0_x, _0_x, DAST.Format.BinaryOpFormat.create_NoFormat()), _0_x)); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_TypeAscription(_0_x, RAST.Type.create_I128()), _0_x)); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_LiteralInt(Dafny.Sequence.UnicodeFromString("1")), _0_x)); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_LiteralString(Dafny.Sequence.UnicodeFromString("2"), true, false), _0_x)); + RASTCoverage.__default.TestNoOptimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_LiteralString(Dafny.Sequence.UnicodeFromString("3"), false, true), _0_x)); + RASTCoverage.__default.AssertCoverage(object.Equals(RASTCoverage.__default.Optimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("z"), Std.Wrappers.Option.create_Some(RAST.Type.create_I128()), Std.Wrappers.Option.create_None()), RAST.Expr.create_StmtExpr(RAST.__default.AssignVar(Dafny.Sequence.UnicodeFromString("z"), _1_y), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("return"))))), RAST.Expr.create_StmtExpr(RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("z"), Std.Wrappers.Option.create_Some(RAST.Type.create_I128()), Std.Wrappers.Option.create_Some(_1_y)), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("return"))))); + Dafny.ISequence _2_coverageExpression; + _2_coverageExpression = Dafny.Sequence.FromElements(RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("abc")), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), RAST.Expr.create_Match(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), Dafny.Sequence.FromElements(RAST.MatchCase.create(Dafny.Sequence.UnicodeFromString("abc"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x"))))), RAST.Expr.create_StmtExpr(RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("panic!()")), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("a"))), RAST.Expr.create_Block(RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("abc"))), RAST.Expr.create_StructBuild(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("dummy")), Dafny.Sequence.FromElements(RAST.AssignIdentifier.create(Dafny.Sequence.UnicodeFromString("foo"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("bar"))))), RAST.Expr.create_StructBuild(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("dummy")), Dafny.Sequence.FromElements(RAST.AssignIdentifier.create(Dafny.Sequence.UnicodeFromString("foo"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("bar"))), RAST.AssignIdentifier.create(Dafny.Sequence.UnicodeFromString("foo2"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("bar"))))), RAST.Expr.create_Tuple(Dafny.Sequence.FromElements(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")))), RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("-"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), DAST.Format.UnaryOpFormat.create_NoFormat()), RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("+"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("y")), DAST.Format.BinaryOpFormat.create_NoFormat()), RAST.Expr.create_TypeAscription(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), RAST.Type.create_I128()), RAST.Expr.create_LiteralInt(Dafny.Sequence.UnicodeFromString("322")), RAST.Expr.create_LiteralString(Dafny.Sequence.UnicodeFromString("abc"), true, false), RAST.Expr.create_LiteralString(Dafny.Sequence.UnicodeFromString("abc"), false, true), RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("abc"), Std.Wrappers.Option.create_Some(RAST.Type.create_I128()), Std.Wrappers.Option.create_None()), RAST.Expr.create_DeclareVar(RAST.DeclareType.create_CONST(), Dafny.Sequence.UnicodeFromString("abc"), Std.Wrappers.Option.create_None(), Std.Wrappers.Option.create_Some(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")))), RAST.__default.AssignVar(Dafny.Sequence.UnicodeFromString("abc"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x"))), RAST.Expr.create_IfExpr(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x"))), RAST.Expr.create_Loop(Std.Wrappers.Option.create_Some(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x"))), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x"))), RAST.Expr.create_For(Dafny.Sequence.UnicodeFromString("abc"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x"))), RAST.Expr.create_Labelled(Dafny.Sequence.UnicodeFromString("abc"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x"))), RAST.Expr.create_Break(Std.Wrappers.Option>.create_None()), RAST.Expr.create_Break(Std.Wrappers.Option>.create_Some(Dafny.Sequence.UnicodeFromString("l"))), RAST.Expr.create_Continue(Std.Wrappers.Option>.create_None()), RAST.Expr.create_Continue(Std.Wrappers.Option>.create_Some(Dafny.Sequence.UnicodeFromString("l"))), RAST.Expr.create_Return(Std.Wrappers.Option.create_None()), RAST.Expr.create_Return(Std.Wrappers.Option.create_Some(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")))), RAST.Expr.create_Call(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), Dafny.Sequence.FromElements()), RAST.Expr.create_Call(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), Dafny.Sequence.FromElements(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("y")))), RAST.Expr.create_CallType(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), Dafny.Sequence.FromElements(RAST.Type.create_I128(), RAST.Type.create_U32())), RAST.Expr.create_Select(RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")), Dafny.Sequence.UnicodeFromString("abc")), RAST.Expr.create_ExprFromPath(RAST.Path.create_PMemberSelect(RAST.Path.create_Crate(), Dafny.Sequence.UnicodeFromString("abc"))), RAST.Expr.create_ExprFromPath(RAST.Path.create_PMemberSelect(RAST.Path.create_Global(), Dafny.Sequence.UnicodeFromString("abc")))); + BigInteger _hi0 = new BigInteger((_2_coverageExpression).Count); + for (BigInteger _3_i = BigInteger.Zero; _3_i < _hi0; _3_i++) { + RAST._IExpr _4_c; + _4_c = (_2_coverageExpression).Select(_3_i); + RAST._IPrintingInfo _5___v0; + _5___v0 = (_4_c).printingInfo; + RAST._IExpr _6___v1; + _6___v1 = RASTCoverage.__default.Optimize(_4_c); + Dafny.IMap> _7___v2; + _7___v2 = Dafny.Map>.FromElements(new Dafny.Pair>(_4_c, (_4_c)._ToString(Dafny.Sequence.UnicodeFromString("")))); + RAST._IExpr _8___v3; + _8___v3 = RASTCoverage.__default.Optimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("abc"), Std.Wrappers.Option.create_Some(RAST.Type.create_I128()), Std.Wrappers.Option.create_None()), _4_c)); + RAST._IExpr _9___v4; + _9___v4 = RASTCoverage.__default.Optimize(RAST.Expr.create_StmtExpr(RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("abc"), Std.Wrappers.Option.create_Some(RAST.Type.create_I128()), Std.Wrappers.Option.create_None()), RAST.Expr.create_StmtExpr(RAST.__default.AssignVar(Dafny.Sequence.UnicodeFromString("abc"), _4_c), RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString(""))))); + RAST._IExpr _10___v5; + _10___v5 = RASTCoverage.__default.Optimize(RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("&"), _4_c, DAST.Format.UnaryOpFormat.create_NoFormat())); + RAST._IExpr _11___v6; + _11___v6 = RASTCoverage.__default.Optimize(RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("!"), _4_c, DAST.Format.UnaryOpFormat.create_NoFormat())); + RAST._IExpr _12___v7; + _12___v7 = RASTCoverage.__default.Optimize(RASTCoverage.__default.ConversionNum(RAST.Type.create_U8(), _4_c)); + RAST._IExpr _13___v8; + _13___v8 = RASTCoverage.__default.Optimize(RASTCoverage.__default.ConversionNum(RAST.Type.create_U8(), RAST.Expr.create_Call(_4_c, Dafny.Sequence.FromElements()))); + Std.Wrappers._IOption> _14___v9; + _14___v9 = (_4_c).RightMostIdentifier(); + } + } + public static void TestPrintingInfo() + { + RAST._IExpr _0_x; + _0_x = RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x")); + RAST._IExpr _1_y; + _1_y = RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("y")); + DAST.Format._IBinaryOpFormat _2_bnf; + _2_bnf = DAST.Format.BinaryOpFormat.create_NoFormat(); + RASTCoverage.__default.AssertCoverage(((RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("x"))).printingInfo).is_UnknownPrecedence); + RASTCoverage.__default.AssertCoverage(object.Equals((_0_x).printingInfo, RAST.PrintingInfo.create_Precedence(BigInteger.One))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_LiteralInt(Dafny.Sequence.UnicodeFromString("3"))).printingInfo, RAST.PrintingInfo.create_Precedence(BigInteger.One))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_LiteralString(Dafny.Sequence.UnicodeFromString("abc"), true, false)).printingInfo, RAST.PrintingInfo.create_Precedence(BigInteger.One))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("?"), _0_x, DAST.Format.UnaryOpFormat.create_NoFormat())).printingInfo, RAST.PrintingInfo.create_SuffixPrecedence(new BigInteger(5)))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("-"), _0_x, DAST.Format.UnaryOpFormat.create_NoFormat())).printingInfo, RAST.PrintingInfo.create_Precedence(new BigInteger(6)))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("*"), _0_x, DAST.Format.UnaryOpFormat.create_NoFormat())).printingInfo, RAST.PrintingInfo.create_Precedence(new BigInteger(6)))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("!"), _0_x, DAST.Format.UnaryOpFormat.create_NoFormat())).printingInfo, RAST.PrintingInfo.create_Precedence(new BigInteger(6)))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("&"), _0_x, DAST.Format.UnaryOpFormat.create_NoFormat())).printingInfo, RAST.PrintingInfo.create_Precedence(new BigInteger(6)))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("&mut"), _0_x, DAST.Format.UnaryOpFormat.create_NoFormat())).printingInfo, RAST.PrintingInfo.create_Precedence(new BigInteger(6)))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_UnaryOp(Dafny.Sequence.UnicodeFromString("!!"), _0_x, DAST.Format.UnaryOpFormat.create_NoFormat())).printingInfo, RAST.PrintingInfo.create_UnknownPrecedence())); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_Select(_0_x, Dafny.Sequence.UnicodeFromString("name"))).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(2), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_ExprFromPath(RAST.Path.create_PMemberSelect(RAST.Path.create_Global(), Dafny.Sequence.UnicodeFromString("name")))).printingInfo, RAST.PrintingInfo.create_Precedence(new BigInteger(2)))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_Call(_0_x, Dafny.Sequence.FromElements())).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(2), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_TypeAscription(_0_x, RAST.Type.create_I128())).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(10), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("*"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(20), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("/"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(20), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("%"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(20), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("+"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(30), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("-"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(30), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("<<"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(40), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString(">>"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(40), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("&"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(50), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("^"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(60), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("|"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(70), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("=="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(80), RAST.Associativity.create_RequiresParentheses()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("!="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(80), RAST.Associativity.create_RequiresParentheses()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("<"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(80), RAST.Associativity.create_RequiresParentheses()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString(">"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(80), RAST.Associativity.create_RequiresParentheses()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("<="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(80), RAST.Associativity.create_RequiresParentheses()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString(">="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(80), RAST.Associativity.create_RequiresParentheses()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("&&"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(90), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("||"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(100), RAST.Associativity.create_LeftToRight()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString(".."), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RequiresParentheses()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("..="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RequiresParentheses()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("+="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("-="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("*="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("/="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("%="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("&="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("|="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("^="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("<<="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString(">>="), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(new BigInteger(110), RAST.Associativity.create_RightToLeft()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_BinaryOp(Dafny.Sequence.UnicodeFromString("?!?"), _0_x, _1_y, _2_bnf)).printingInfo, RAST.PrintingInfo.create_PrecedenceAssociativity(BigInteger.Zero, RAST.Associativity.create_RequiresParentheses()))); + RASTCoverage.__default.AssertCoverage(object.Equals((RAST.Expr.create_Break(Std.Wrappers.Option>.create_None())).printingInfo, RAST.PrintingInfo.create_UnknownPrecedence())); + } + public static void TestNoExtraSemicolonAfter() + { + RASTCoverage.__default.AssertCoverage((RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString(";"))).NoExtraSemicolonAfter()); + RASTCoverage.__default.AssertCoverage(!((RAST.Expr.create_RawExpr(Dafny.Sequence.UnicodeFromString("a"))).NoExtraSemicolonAfter())); + RASTCoverage.__default.AssertCoverage((RAST.Expr.create_Return(Std.Wrappers.Option.create_None())).NoExtraSemicolonAfter()); + RASTCoverage.__default.AssertCoverage((RAST.Expr.create_Continue(Std.Wrappers.Option>.create_None())).NoExtraSemicolonAfter()); + RASTCoverage.__default.AssertCoverage((RAST.Expr.create_Break(Std.Wrappers.Option>.create_None())).NoExtraSemicolonAfter()); + RASTCoverage.__default.AssertCoverage((RAST.__default.AssignVar(Dafny.Sequence.UnicodeFromString("x"), RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("y")))).NoExtraSemicolonAfter()); + RASTCoverage.__default.AssertCoverage((RAST.Expr.create_DeclareVar(RAST.DeclareType.create_MUT(), Dafny.Sequence.UnicodeFromString("x"), Std.Wrappers.Option.create_None(), Std.Wrappers.Option.create_None())).NoExtraSemicolonAfter()); + RASTCoverage.__default.AssertCoverage(!((RAST.Expr.create_Identifier(Dafny.Sequence.UnicodeFromString("x"))).NoExtraSemicolonAfter())); + } + } +} // end of namespace RASTCoverage \ No newline at end of file diff --git a/Source/DafnyCore.Test/NodeTests.cs b/Source/DafnyCore.Test/NodeTests.cs index e4a52d860f1..fd613cd726d 100644 --- a/Source/DafnyCore.Test/NodeTests.cs +++ b/Source/DafnyCore.Test/NodeTests.cs @@ -1,31 +1,30 @@ using System.Collections.Concurrent; using Microsoft.Dafny; -namespace DafnyCore.Test; +namespace DafnyCore.Test; public class NodeTests { class ConcreteNode : Node { - public ConcreteNode(RangeToken rangeToken, IEnumerable? children = null) { - RangeToken = rangeToken; + public ConcreteNode(IOrigin origin, IEnumerable? children = null) { + Origin = origin; Children = children ?? Enumerable.Empty(); } - public override RangeToken RangeToken { get; set; } - public override IToken Tok => RangeToken.StartToken; + public override IOrigin Origin { get; } public override IEnumerable Children { get; } public override IEnumerable PreResolveChildren => Children; } - private static RangeToken CreateRange(Uri uri, int startLine, int startColumn, int endLine, int endColumn) { - return new RangeToken(new Token(startLine + 1, startColumn + 1) { Uri = uri }, new Token(endLine + 1, endColumn + 1) { Uri = uri }); + private static SourceOrigin CreateRange(Uri uri, int startLine, int startColumn, int endLine, int endColumn) { + return new SourceOrigin(new Token(startLine + 1, startColumn + 1) { Uri = uri }, new Token(endLine + 1, endColumn + 1) { Uri = uri }); } [Fact] public void FindNodeWithTokenLessIntermediate() { var uri = new Uri(Directory.GetCurrentDirectory()); var child = new ConcreteNode(CreateRange(uri, 0, 1, 0, 2)); - var parent = new ConcreteNode(RangeToken.NoToken, (IEnumerable)new INode[] { child }); + var parent = new ConcreteNode(SourceOrigin.NoToken, (IEnumerable)new INode[] { child }); var grandParent = new ConcreteNode(CreateRange(uri, 0, 0, 0, 3), (IEnumerable)new INode[] { parent }); var shouldBeChild = grandParent.FindNode(uri, new DafnyPosition(0, 1)); @@ -35,7 +34,7 @@ public void FindNodeWithTokenLessIntermediate() { [Fact] public void SkipTokenlessLeaf() { var uri = new Uri(Directory.GetCurrentDirectory()); - var child1 = new ConcreteNode(RangeToken.NoToken); + var child1 = new ConcreteNode(SourceOrigin.NoToken); var child2 = new ConcreteNode(CreateRange(uri, 0, 1, 0, 2)); var parent = new ConcreteNode(CreateRange(uri, 0, 0, 0, 3), (IEnumerable)new[] { child1, child2 }); diff --git a/Source/DafnyCore.Test/WriterFromOutputHelper.cs b/Source/DafnyCore.Test/WriterFromOutputHelper.cs new file mode 100644 index 00000000000..a7039f46f01 --- /dev/null +++ b/Source/DafnyCore.Test/WriterFromOutputHelper.cs @@ -0,0 +1,53 @@ +#nullable enable +using System.Text; +using Xunit.Abstractions; + +namespace DafnyCore.Test; + +public class WriterFromOutputHelper : TextWriter { + private readonly StringBuilder buffer = new(); + private readonly ITestOutputHelper output; + + public WriterFromOutputHelper(ITestOutputHelper output) { + this.output = output; + } + + public override void Write(string? value) { + if (value != null) { + buffer.Append(value); + } + } + + public override void Write(char value) { + buffer.Append(value); + } + + public override Encoding Encoding => Encoding.Default; + + public override void WriteLine(string? value) { + try { + output.WriteLine(buffer + value); + } catch { + // ignored because of https://github.com/dafny-lang/dafny/issues/5723 + } + buffer.Clear(); + } + + public override void WriteLine(string format, params object?[] arg) { + try { + output.WriteLine(buffer + format, arg); + } catch { + // ignored because of https://github.com/dafny-lang/dafny/issues/5723 + } + buffer.Clear(); + } + + public override void Flush() { + try { + output.WriteLine(buffer.ToString()); + } catch { + // ignored because of https://github.com/dafny-lang/dafny/issues/5723 + } + base.Flush(); + } +} \ No newline at end of file diff --git a/Source/DafnyCore.Test/coverlet.runsettings b/Source/DafnyCore.Test/coverlet.runsettings new file mode 100644 index 00000000000..11c8afc63f8 --- /dev/null +++ b/Source/DafnyCore.Test/coverlet.runsettings @@ -0,0 +1,12 @@ + + + + + + + [*]DAST.*,[*]DCOMP.* + + + + + diff --git a/Source/DafnyCore/AST/AstVisitor.cs b/Source/DafnyCore/AST/AstVisitor.cs index 9f5ea2ef5f9..08bcd71bf56 100644 --- a/Source/DafnyCore/AST/AstVisitor.cs +++ b/Source/DafnyCore/AST/AstVisitor.cs @@ -136,16 +136,17 @@ public virtual void VisitFunction(Function function) { VisitAttributes(function, function.EnclosingClass.EnclosingModuleDefinition); - foreach (var formal in function.Formals) { + foreach (var formal in function.Ins) { VisitUserProvidedType(formal.Type, context); } VisitUserProvidedType(function.ResultType, context); - VisitDefaultParameterValues(function.Formals, context); + VisitDefaultParameterValues(function.Ins, context); function.Req.ForEach(aexpr => VisitAttributedExpression(aexpr, context)); - function.Reads.ForEach(frameExpression => VisitTopLevelFrameExpression(frameExpression, context)); + VisitAttributes(function.Reads, function.EnclosingClass.EnclosingModuleDefinition); + function.Reads.Expressions.ForEach(frameExpression => VisitTopLevelFrameExpression(frameExpression, context)); function.Ens.ForEach(aexpr => VisitAttributedExpression(aexpr, GetContext(function, true))); @@ -173,6 +174,8 @@ public virtual void VisitMethod(Method method) { method.Req.ForEach(aexpr => VisitAttributedExpression(aexpr, context)); + method.Reads.Expressions.ForEach(frameExpression => VisitTopLevelFrameExpression(frameExpression, context)); + VisitAttributes(method.Mod, method.EnclosingClass.EnclosingModuleDefinition); method.Mod.Expressions.ForEach(frameExpression => VisitTopLevelFrameExpression(frameExpression, context)); @@ -325,15 +328,15 @@ protected virtual void VisitStatement(Statement stmt, VisitorContext context) { // Visit user-provided types if (stmt is VarDeclStmt varDeclStmt) { foreach (var local in varDeclStmt.Locals) { - VisitUserProvidedType(local.OptionalType, context); + VisitUserProvidedType(local.SyntacticType, context); } } else if (stmt is VarDeclPattern varDeclPattern) { foreach (var local in varDeclPattern.LocalVars) { - VisitUserProvidedType(local.OptionalType, context); + VisitUserProvidedType(local.SyntacticType, context); } - } else if (stmt is AssignStmt assignStmt) { + } else if (stmt is SingleAssignStmt assignStmt) { if (assignStmt.Rhs is TypeRhs typeRhs) { if (typeRhs.EType != null) { VisitUserProvidedType(typeRhs.EType, context); diff --git a/Source/DafnyCore/AST/Attributes.cs b/Source/DafnyCore/AST/Attributes.cs index e063f267ec7..decc10cf47b 100644 --- a/Source/DafnyCore/AST/Attributes.cs +++ b/Source/DafnyCore/AST/Attributes.cs @@ -6,7 +6,46 @@ namespace Microsoft.Dafny; -public class Attributes : TokenNode { +static class AttributeBearingDeclaration { + + public static bool IsExtern(this IAttributeBearingDeclaration me, DafnyOptions options) => + options.AllowExterns && Attributes.Contains(me.Attributes, "extern"); + + public static bool IsExplicitAxiom(this IAttributeBearingDeclaration me) => + Attributes.Contains(me.Attributes, "axiom"); +} + +// Syntax of a formal of a built-in @-attribute +// To create one, prefer using the chaining BuiltInAtAttributeSyntax.WithArg() +public record BuiltInAtAttributeArgSyntax( + string ArgName, + Type ArgType, // If null, it means it's not resolved (@Induction and @Trigger) + Expression DefaultValue) { + public Formal ToFormal() { + Contract.Assert(ArgType != null); + return new Formal(Token.NoToken, ArgName, ArgType, true, false, + DefaultValue); + } +} + +// Syntax for a built-in @-attribute. +// To create such an attribute, use the Attributes.B() helper +public record BuiltInAtAttributeSyntax( + string Name, + List Args, + Func CanBeApplied) { + public BuiltInAtAttributeSyntax WithArg(String argName, Type argType, Expression defaultValue = null) { + var c = new List(Args) { + new(argName, argType, defaultValue) }; + return this with { Args = c }; + } + + public BuiltInAtAttributeSyntax Filter(Func canBeApplied) { + return this with { CanBeApplied = canBeApplied }; + } +} + +public class Attributes : NodeWithComputedRange, ICanFormat { [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Name != null); @@ -15,6 +54,7 @@ void ObjectInvariant() { public static string AxiomAttributeName = "axiom"; public static string ConcurrentAttributeName = "concurrent"; + public static string AssumeConcurrentAttributeName = "assume_concurrent"; public static string ExternAttributeName = "extern"; public static string VerifyAttributeName = "verify"; public static string AutoGeneratedAttributeName = "auto_generated"; @@ -24,9 +64,10 @@ void ObjectInvariant() { public readonly List Args; public readonly Attributes Prev; - public Attributes(string name, [Captured] List args, Attributes prev) { + public Attributes(string name, [Captured] List args, Attributes prev) : base(Token.NoToken) { Contract.Requires(name != null); Contract.Requires(cce.NonNullElements(args)); + Contract.Requires(name != UserSuppliedAtAttribute.AtName || this is UserSuppliedAtAttribute); Name = name; Args = args; Prev = prev; @@ -34,7 +75,7 @@ public Attributes(string name, [Captured] List args, Attributes prev public override string ToString() { string result = Prev?.ToString() + "{:" + Name; - if (Args == null || Args.Count() == 0) { + if (Args == null || !Args.Any()) { return result + "}"; } else { var exprs = String.Join(", ", Args.Select(e => e.ToString())); @@ -88,7 +129,7 @@ public static bool ContainsBool(Attributes attrs, string nm, ref bool value) { /// the enclosing class, or any enclosing module. Settings closer to the declaration /// override those further away. /// - public static bool ContainsBoolAtAnyLevel(MemberDecl decl, string attribName) { + public static bool ContainsBoolAtAnyLevel(MemberDecl decl, string attribName, bool defaultVal = false) { bool setting = true; if (Attributes.ContainsBool(decl.Attributes, attribName, ref setting)) { return setting; @@ -107,7 +148,7 @@ public static bool ContainsBoolAtAnyLevel(MemberDecl decl, string attribName) { mod = mod.EnclosingModule; } - return false; + return defaultVal; } /// @@ -197,6 +238,438 @@ public static bool ContainsMatchingValue(Attributes attrs, string nm, ref object : new List { Prev }); public override IEnumerable PreResolveChildren => Children; + public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { + foreach (var token in OwnedTokens) { + switch (token.val) { + case "}": { + formatter.SetClosingIndentedRegion(token, indentBefore); + break; + } + case "@": { + formatter.SetIndentations(token, indentBefore, indentBefore, indentBefore); + break; + } + case ",": { + formatter.SetDelimiterInsideIndentedRegions(token, indentBefore); + break; + } + case "{" or "{:": { + formatter.SetOpeningIndentedRegion(token, indentBefore); + break; + } + } + } + foreach (var arg in Args) { + formatter.SetExpressionIndentation(arg); + } + formatter.SetClosingIndentedRegion(EndToken, indentBefore); + return false; // Don't do inner attributes, they should be performed independently + } + + // Typically, {:} are indented when @-attributes are not + public static void SetIndents(Attributes attrs, int indentBefore, TokenNewIndentCollector formatter) { + foreach (var attribute in attrs.AsEnumerable()) { + if (attribute.StartToken.val == UserSuppliedAtAttribute.AtName) { + attribute.SetIndent(indentBefore, formatter); + } else { + attribute.SetIndent(indentBefore + formatter.SpaceTab, formatter); + } + } + } + public static string TupleItem0Name => "0"; + // Helper to create a built-in @-attribute + static BuiltInAtAttributeSyntax BuiltIn(string name) { + return new BuiltInAtAttributeSyntax( + name, new List(), _ => true); + } + + // Helper to create an old-style attribute + private static Attributes A(string name, params Expression[] args) { + return new Attributes(name, args.Select(arg => + arg is DefaultValueExpression defaultExpr ? defaultExpr.Resolved : arg).ToList(), null); + } + + // Helper to create an old-style attribute with only one argument + private static Attributes A1(string name, ActualBindings bindings) { + if (Get(bindings, 0, out var expr) && expr != null) { + return A(name, expr); + } + + return A(name); + } + + // Given a user-supplied @-attribute, expand it if recognized as builtin to an old-style attribute + // or mark it as not built-in for later resolution + public static Attributes ExpandAtAttribute(Program program, UserSuppliedAtAttribute atAttribute, IAttributeBearingDeclaration attributeHost) { + var toMatch = atAttribute.Arg; + var name = atAttribute.UserSuppliedName; + var bindings = atAttribute.UserSuppliedPreResolveBindings; + + if (name == null) { + program.Reporter.Error(MessageSource.Resolver, atAttribute.Origin, "Attribute not recognized: " + atAttribute.ToString()); + return null; + } + + if (!TryGetBuiltinAtAttribute(name, out var builtinSyntax) || builtinSyntax == null) { + atAttribute.Builtin = false; // Will be resolved after + return null; + } + + if (!builtinSyntax.CanBeApplied(attributeHost)) { + program.Reporter.Error(MessageSource.Resolver, atAttribute.Origin, UserSuppliedAtAttribute.AtName + atAttribute.UserSuppliedName + " attribute cannot be applied to " + attributeHost.WhatKind); + } + + var resolver = new ModuleResolver(new ProgramResolver(program), program.Options) { + reporter = program.Reporter + }; + resolver.moduleInfo = resolver.ProgramResolver.SystemModuleManager.systemNameInfo; + var formals = builtinSyntax.Args.Select(arg => arg.ToFormal()).ToArray(); + ResolveLikeDatatypeConstructor(program, formals, name, atAttribute, bindings, resolver); + + atAttribute.Builtin = true; + atAttribute.Arg.Type = Type.Int; // Dummy type to avoid crashes + var intDecl = resolver.SystemModuleManager.valuetypeDecls.First(valueTypeDecl => valueTypeDecl.Name == PreType.TypeNameInt); + + atAttribute.Arg.PreType = new DPreType(intDecl, new List(), null); + + switch (name) { + case "AssumeCrossModuleTermination": { + return A("AssumeCrossModuleTermination"); + } + case "AutoContracts": { + return A("autocontracts"); + } + case "AutoRequires": { + return A("autoReq"); + } + case "AutoRevealDependenciesAll": { + return A1("autoRevealDependencies", bindings); + } + case "AutoRevealDependencies": { + return A1("autoRevealDependencies", bindings); + } + case "Axiom": { + return A(AxiomAttributeName); + } + case "Compile": { + return A1("compile", bindings); + } + case "Concurrent": { + return A(ConcurrentAttributeName); + } + case "DisableNonlinearArithmetic": { + return A1("disableNonlinearArithmetic", bindings); + } + case "Fuel": { + if (Get(bindings, 0, out var lowFuel) && lowFuel != null) { + if (Get(bindings, 1, out var highFuel) && highFuel != null) { + if (Get(bindings, 2, out var functionName) && IsStringNotEmpty(functionName)) { + return A("fuel", functionName, lowFuel, highFuel); + } + + return A("fuel", lowFuel, highFuel); + } + + return A("fuel", lowFuel); + } + + return A("fuel"); + } + case "IsolateAssertions": { + return A("isolate_assertions"); + } + case "NativeUInt8": { + return A("nativeType", DefaultString("byte")); + } + case "NativeInt8": { + return A("nativeType", DefaultString("sbyte")); + } + case "NativeUInt16": { + return A("nativeType", DefaultString("ushort")); + } + case "NativeInt16": { + return A("nativeType", DefaultString("short")); + } + case "NativeUInt32": { + return A("nativeType", DefaultString("uint")); + } + case "NativeInt32": { + return A("nativeType", DefaultString("int")); + } + case "NativeInt53": { + return A("nativeType", DefaultString("number")); + } + case "NativeUInt64": { + return A("nativeType", DefaultString("ulong")); + } + case "NativeInt64": { + return A("nativeType", DefaultString("long")); + } + case "NativeUInt128": { + return A("nativeType", DefaultString("udoublelong")); + } + case "NativeInt128": { + return A("nativeType", DefaultString("doublelong")); + } + case "NativeInt": { + return A("nativeType", DefaultBool(true)); + } + case "NativeNone": { + return A("nativeType", DefaultBool(false)); + } + case "NativeIntOrReal": { + return A("nativeType"); + } + case "Options": { + return A1("options", bindings); + } + case "Print": { + return A("print"); + } + case "Priority": { + return A1("priority", bindings); + } + case "ResourceLimit": { + return A1("resource_limit", bindings); + } + case "Synthesize": { + return A("synthesize"); + } + case "TimeLimit": { + return A1("timeLimit", bindings); + } + case "TimeLimitMultiplier": { + return A1("timeLimitMultiplier", bindings); + } + case "TailRecursion": { + return A("tailrecursion"); + } + case "Test": { + return A("test"); + } + case "TestEntry": { + return A("TestEntry"); + } + case "TestInline": { + return A1("testInline", bindings); + } + case "Transparent": { + return A("transparent"); + } + case "VcsMaxCost": { + return A1("vcs_max_cost", bindings); + } + case "VcsMaxKeepGoingSplits": { + return A1("vcs_max_keep_going_splits", bindings); + } + case "VcsMaxSplits": { + return A1("vcs_max_splits", bindings); + } + case "Verify": { + return A1(VerifyAttributeName, bindings); + } + case "VerifyOnly": { + return A("only"); + } + default: { + throw new Exception("@-Attribute added to Attributes.BuiltinAtAttributes needs to be handled here"); + } + } + } + + // List of built-in @-attributes with their definitions. + // This list could be obtained from parsing and resolving a .Dfy file + // but for now it's good enough. + public static readonly List BuiltinAtAttributes = new() { + BuiltIn("AssumeCrossModuleTermination") + .Filter(attributeHost => attributeHost is ClassDecl or TraitDecl), + BuiltIn("AutoContracts") + .Filter(attributeHost => attributeHost is ClassDecl), + BuiltIn("AutoRequires") + .Filter(attributeHost => attributeHost is Function), + BuiltIn("AutoRevealDependenciesAll").WithArg(TupleItem0Name, Type.Bool, DefaultBool(true)) + .Filter(attributeHost => attributeHost is MethodOrFunction), + BuiltIn("AutoRevealDependencies").WithArg("level", Type.Int) + .Filter(attributeHost => attributeHost is MethodOrFunction), + BuiltIn("Axiom") + .Filter(attributeHost => attributeHost is MethodOrFunction), + BuiltIn("Compile") + .WithArg(TupleItem0Name, Type.Bool, DefaultBool(true)) + .Filter(attributeHost => + attributeHost is TopLevelDecl and not TypeParameter or MemberDecl or ModuleDefinition), + BuiltIn("Concurrent") + .Filter(attributeHost => + attributeHost is MethodOrFunction), + BuiltIn("DisableNonlinearArithmetic") + .WithArg("disable", Type.Bool, DefaultBool(true)) + .Filter(attributeHost => + attributeHost is ModuleDefinition), + BuiltIn("Fuel") + .WithArg("low", Type.Int, DefaultInt(1)) + .WithArg("high", Type.Int, DefaultInt(2)) + .WithArg("functionName", Type.ResolvedString(), DefaultString("")) + .Filter(attributeHost => attributeHost is MethodOrFunction or AssertStmt), + BuiltIn("IsolateAssertions") + .Filter(attributeHost => attributeHost is ICanVerify), + BuiltIn("NativeUInt8") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeInt8") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeUInt16") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeInt16") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeUInt32") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeInt32") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeInt53") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeUInt64") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeInt64") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeUInt128") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeInt128") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeInt") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeNone") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("NativeIntOrReal") + .Filter(attributeHost => attributeHost is NewtypeDecl), + BuiltIn("Options") + .WithArg(TupleItem0Name, Type.ResolvedString()) + .Filter(attributeHost => attributeHost is ModuleDecl or ModuleDefinition), + BuiltIn("Print") + .Filter(attributeHost => attributeHost is Method), + BuiltIn("Priority").WithArg(TupleItem0Name, Type.Int) + .Filter(attributeHost => attributeHost is ICanVerify), + BuiltIn("ResourceLimit").WithArg(TupleItem0Name, Type.ResolvedString()) + .Filter(attributeHost => attributeHost is ICanVerify), + BuiltIn("Synthesize") + .Filter(attributeHost => attributeHost is Method), + BuiltIn("TimeLimit").WithArg(TupleItem0Name, Type.Int) + .Filter(attributeHost => attributeHost is ICanVerify), + BuiltIn("TimeLimitMultiplier").WithArg(TupleItem0Name, Type.Int) + .Filter(attributeHost => attributeHost is ICanVerify), + BuiltIn("TailRecursion") + .Filter(attributeHost => attributeHost is MethodOrFunction), + BuiltIn("Test") + .Filter(attributeHost => attributeHost is MethodOrFunction), + BuiltIn("TestEntry") + .Filter(attributeHost => attributeHost is MethodOrFunction), + BuiltIn("TestInline").WithArg("level", Type.Int, DefaultInt(1)) + .Filter(attributeHost => attributeHost is MethodOrFunction), + BuiltIn("Transparent") + .Filter(attributeHost => attributeHost is Function), + BuiltIn("VcsMaxCost").WithArg(TupleItem0Name, Type.Int) + .Filter(attributeHost => attributeHost is ICanVerify), + BuiltIn("VcsMaxKeepGoingSplits").WithArg(TupleItem0Name, Type.Int) + .Filter(attributeHost => attributeHost is ICanVerify), + BuiltIn("VcsMaxSplits").WithArg(TupleItem0Name, Type.Int) + .Filter(attributeHost => attributeHost is ICanVerify), + BuiltIn("Verify") + .Filter(attributeHost => attributeHost is ICanVerify), + BuiltIn("VerifyOnly") + .Filter(attributeHost => attributeHost is ICanVerify), + }; + + ////// Helpers to create default values for the @-attribute definitions above ////// + + public static LiteralExpr DefaultString(string value) { + return Expression.CreateStringLiteral(Token.NoToken, value); + } + + public static LiteralExpr DefaultBool(bool value) { + return Expression.CreateBoolLiteral(Token.NoToken, value); + } + + public static LiteralExpr DefaultInt(int value) { + return Expression.CreateIntLiteralNonnegative(Token.NoToken, value); + } + + private static bool IsStringNotEmpty(Expression value) { + return value is StringLiteralExpr { Value: string and not "" }; + } + + // Given resolved bindings, gets the i-th argument according to the + // declaration formals order + private static bool Get(ActualBindings bindings, int i, out Expression expr) { + if (bindings.Arguments.Count < i + 1) { + expr = null; + return false; + } + + expr = bindings.Arguments[i]; + return true; + } + + // Resolves bindings given a list of datatype constructor-like formals, + // obtained from built-in @-attribute definitions + private static void ResolveLikeDatatypeConstructor(Program program, Formal[] formals, string attrName, + UserSuppliedAtAttribute attrs, ActualBindings bindings, ModuleResolver resolver) { + var resolutionContext = new ResolutionContext(new NoContext(program.DefaultModuleDef), false); ; + var typeMap = new Dictionary(); + resolver.ResolveActualParameters(bindings, formals.ToList(), attrs.Origin, + attrs, resolutionContext, typeMap, null); + resolver.FillInDefaultValueExpressions(); + resolver.SolveAllTypeConstraints(); + // Verify that provided arguments are given literally + foreach (var binding in bindings.ArgumentBindings) { + if (binding.Actual is not LiteralExpr) { + program.Reporter.Error(MessageSource.Resolver, binding.Actual.Origin, $"Argument to attribute {attrName} must be a literal"); + } + } + } + + // Recovers a built-in @-Attribute if it exists + public static bool TryGetBuiltinAtAttribute(string name, out BuiltInAtAttributeSyntax builtinAtAttribute) { + return BuiltInAtAttributeDictionary.TryGetValue(name, out builtinAtAttribute); + } + + // Builtin @-attributes dictionary based on the sequence of definitions of @-attributes + public static Dictionary BuiltInAtAttributeDictionary = + BuiltinAtAttributes.ToDictionary(b => { + if (b.Name.Contains("_") || b.Name.Contains("-") || Char.IsLower(b.Name[0])) { + throw new Exception("Builtin @-attributes are PascalCase for consistency"); + } + return b.Name; + }, b => b); + + // Overridable method to clone the attribute as if the new attribute was placed after "prev" in the source code + public virtual Attributes CloneAfter(Attributes prev) { + return new Attributes(Name, Args, prev); + } + + //////// Helpers for parsing attributes ////////////////// + + // Returns the memory location's attributes content and set the memory location to null (no attributes) + public static Attributes Consume(ref Attributes tmpStack) { + var result = tmpStack; + tmpStack = null; + return result; + } + + // Empties the first attribute memory location while prepending its attributes to the second attribute memory location, in the same order + public static void MergeInto(ref Attributes tmpStack, ref Attributes attributesStack) { + MergeIntoReadonly(tmpStack, ref attributesStack); + tmpStack = null; + } + + // Prepends the attributes tmpStack before the attributes contained in the memory location attributesStack + private static void MergeIntoReadonly(Attributes tmpStack, ref Attributes attributesStack) { + if (tmpStack == null) { + return; + } + if (attributesStack == null) { + attributesStack = tmpStack; + return; + } + MergeIntoReadonly(tmpStack.Prev, ref attributesStack); + attributesStack = tmpStack.CloneAfter(attributesStack); + } } public static class AttributesExtensions { @@ -211,30 +684,103 @@ public static IEnumerable AsEnumerable(this Attributes attr) { } } +// {:..} Attributes parsed are built using this class public class UserSuppliedAttributes : Attributes { - public readonly IToken OpenBrace; - public readonly IToken CloseBrace; + public readonly IOrigin OpenBrace; + public readonly IOrigin CloseBrace; public bool Recognized; // set to true to indicate an attribute that is processed by some part of Dafny; this allows it to be colored in the IDE - public UserSuppliedAttributes(IToken tok, IToken openBrace, IToken closeBrace, List args, Attributes prev) - : base(tok.val, args, prev) { - Contract.Requires(tok != null); + public UserSuppliedAttributes(IOrigin origin, IOrigin openBrace, IOrigin closeBrace, List args, Attributes prev) + : base(origin.val, args, prev) { + Contract.Requires(origin != null); Contract.Requires(openBrace != null); Contract.Requires(closeBrace != null); Contract.Requires(args != null); - this.tok = tok; + SetOrigin(origin); OpenBrace = openBrace; CloseBrace = closeBrace; } } +// @-Attributes parsed are built using this class +public class UserSuppliedAtAttribute : Attributes { + public static readonly string AtName = "@"; + public readonly IOrigin AtSign; + public bool Builtin; // set to true to indicate it was recognized as a builtin attribute + // Otherwise it's a user-defined one and Arg needs to be fully resolved + public UserSuppliedAtAttribute(IOrigin origin, Expression arg, Attributes prev) + : base(AtName, new List() { arg }, prev) { + Contract.Requires(origin != null); + SetOrigin(origin); + this.AtSign = origin; + } + + public Expression Arg => Args[0]; + + public override Attributes CloneAfter(Attributes prev) { + return new UserSuppliedAtAttribute(AtSign, Args[0], prev); + } + + // Name of this @-Attribute, which is the part right after the @ + public string UserSuppliedName => + GetName(this); + + // Pre-resolved bindings of this @-Attribute + public ActualBindings UserSuppliedPreResolveBindings => + GetPreResolveBindings(this); + + // Pre-resolved arguments of this @-Attributes. The order is the one provided by the user, + // not by any binding. Used for + public IEnumerable UserSuppliedPreResolveArguments => + GetPreResolveArguments(this); + + // Gets the name of an @-attribute. Attributes might be applied. + public static string GetName(Attributes a) { + if (a is UserSuppliedAtAttribute { Arg: ApplySuffix { Lhs: NameSegment { Name: var name } } }) { + return name; + } + if (a is UserSuppliedAtAttribute { Arg: NameSegment { Name: var singleName } }) { + return singleName; + } + + return null; + } + + // Gets the pre-resolved bindings of an @-Attribute. + // Returns an empty bindings if it's anything else + public static ActualBindings GetPreResolveBindings(Attributes a) { + if (a is UserSuppliedAtAttribute { Arg: ApplySuffix { Bindings: var bindings } }) { + return bindings; + } + return new ActualBindings(new List()); + } + + // Gets the list of pre-resolved arguments of an @-Attribute, and an empty list otherwise + public static IEnumerable GetPreResolveArguments(Attributes a) { + if (a is UserSuppliedAtAttribute { UserSuppliedPreResolveBindings: var bindings }) { + return bindings.ArgumentBindings.Select(arg => arg.Actual); + } + + return new List(); + } + + // Gets the list of pre-resolved arguments of an @-Attribute, and the list of arguments + // for any other kind of attributes. Used for example to extract module options for parsing. + public static IEnumerable GetUserSuppliedArguments(Attributes a) { + return a is UserSuppliedAtAttribute { UserSuppliedPreResolveArguments: var arguments } ? arguments : a.Args; + } + + public override string ToString() => Prev + (Prev == null ? "" : " ") + "@" + Arg; +} + /// /// A class implementing this interface is one that can carry attributes. /// public interface IAttributeBearingDeclaration { - Attributes Attributes { get; } + Attributes Attributes { get; internal set; } + string WhatKind { get; } } -public static class IAttributeBearingDeclarationExtensions { +public static class AttributeBearingDeclarationExtensions { public static bool HasUserAttribute(this IAttributeBearingDeclaration decl, string name, out Attributes attribute) { if (Attributes.Find(decl.Attributes, name) is UserSuppliedAttributes attr) { attribute = attr; diff --git a/Source/DafnyCore/AST/BottomUpVisitor.cs b/Source/DafnyCore/AST/BottomUpVisitor.cs index 00d9274beeb..ba784a6ed83 100644 --- a/Source/DafnyCore/AST/BottomUpVisitor.cs +++ b/Source/DafnyCore/AST/BottomUpVisitor.cs @@ -56,6 +56,7 @@ public void Visit(NewtypeDecl ntd) { } public void Visit(Method method) { Visit(method.Req); + Visit(method.Reads.Expressions); Visit(method.Mod.Expressions); Visit(method.Ens); Visit(method.Decreases.Expressions); @@ -64,7 +65,7 @@ public void Visit(Method method) { } public void Visit(Function function) { Visit(function.Req); - Visit(function.Reads); + Visit(function.Reads.Expressions); Visit(function.Ens); Visit(function.Decreases.Expressions); if (function.Body != null) { Visit(function.Body); } diff --git a/Source/DafnyCore/AST/Cloner.cs b/Source/DafnyCore/AST/Cloner.cs index 615ab2a33c9..6a8c76b5634 100644 --- a/Source/DafnyCore/AST/Cloner.cs +++ b/Source/DafnyCore/AST/Cloner.cs @@ -66,7 +66,7 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne if (d is AbstractTypeDecl) { var dd = (AbstractTypeDecl)d; - return new AbstractTypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, + return new AbstractTypeDecl(Origin(dd.Origin), dd.NameNode.Clone(this), newParent, CloneTPChar(dd.Characteristics), dd.TypeArgs.ConvertAll(CloneTypeParam), dd.ParentTraits.ConvertAll(CloneType), dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining); @@ -75,23 +75,24 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne !(d is NonNullTypeDecl)); // don't clone the non-null type declaration; close the class, which will create a new non-null type declaration var dd = (SubsetTypeDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); - return new SubsetTypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), CloneTPChar(dd.Characteristics), tps, + return new SubsetTypeDecl(Origin(dd.Origin), dd.NameNode.Clone(this), CloneTPChar(dd.Characteristics), tps, newParent, CloneBoundVar(dd.Var, false), CloneExpr(dd.Constraint), dd.WitnessKind, CloneExpr(dd.Witness), CloneAttributes(dd.Attributes)); } else if (d is TypeSynonymDecl) { var dd = (TypeSynonymDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); - return new TypeSynonymDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), CloneTPChar(dd.Characteristics), tps, + return new TypeSynonymDecl(Origin(dd.Origin), dd.NameNode.Clone(this), CloneTPChar(dd.Characteristics), tps, newParent, CloneType(dd.Rhs), CloneAttributes(dd.Attributes)); } else if (d is NewtypeDecl) { var dd = (NewtypeDecl)d; if (dd.Var == null) { - return new NewtypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, CloneType(dd.BaseType), + return new NewtypeDecl(Origin(dd.Origin), dd.NameNode.Clone(this), dd.TypeArgs.ConvertAll(CloneTypeParam), newParent, + CloneType(dd.BaseType), dd.WitnessKind, CloneExpr(dd.Witness), dd.ParentTraits.ConvertAll(CloneType), dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining); } else { - return new NewtypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, CloneBoundVar(dd.Var, false), - CloneExpr(dd.Constraint), dd.WitnessKind, CloneExpr(dd.Witness), + return new NewtypeDecl(Origin(dd.Origin), dd.NameNode.Clone(this), dd.TypeArgs.ConvertAll(CloneTypeParam), newParent, + CloneBoundVar(dd.Var, false), CloneExpr(dd.Constraint), dd.WitnessKind, CloneExpr(dd.Witness), dd.ParentTraits.ConvertAll(CloneType), dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining); } @@ -103,7 +104,7 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne var dd = (IndDatatypeDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); var ctors = dd.Ctors.ConvertAll(CloneCtor); - var dt = new IndDatatypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, tps, ctors, + var dt = new IndDatatypeDecl(Origin(dd.Origin), dd.NameNode.Clone(this), newParent, tps, ctors, dd.ParentTraits.ConvertAll(CloneType), dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining); return dt; @@ -111,7 +112,7 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne var dd = (CoDatatypeDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); var ctors = dd.Ctors.ConvertAll(CloneCtor); - var dt = new CoDatatypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, tps, ctors, + var dt = new CoDatatypeDecl(Origin(dd.Origin), dd.NameNode.Clone(this), newParent, tps, ctors, dd.ParentTraits.ConvertAll(CloneType), dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining); return dt; @@ -128,7 +129,7 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne var ens = dd.Ensures.ConvertAll(CloneAttributedExpr); var yens = dd.YieldEnsures.ConvertAll(CloneAttributedExpr); var body = CloneBlockStmt(dd.Body); - var iter = new IteratorDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, + var iter = new IteratorDecl(Origin(dd.Origin), dd.NameNode.Clone(this), newParent, tps, ins, outs, reads, mod, decr, req, ens, yreq, yens, body, CloneAttributes(dd.Attributes), dd.SignatureEllipsis); @@ -137,7 +138,7 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne var dd = (TraitDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); var mm = dd.Members.ConvertAll(member => CloneMember(member, false)); - var cl = new TraitDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, tps, mm, + var cl = new TraitDecl(Origin(dd.Origin), dd.NameNode.Clone(this), newParent, tps, mm, CloneAttributes(dd.Attributes), dd.IsRefining, dd.ParentTraits.ConvertAll(CloneType)); return cl; } else if (d is DefaultClassDecl) { @@ -149,7 +150,7 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne var dd = (ClassDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); var mm = dd.Members.ConvertAll(member => CloneMember(member, false)); - return new ClassDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, tps, mm, + return new ClassDecl(Origin(dd.Origin), dd.NameNode.Clone(this), newParent, tps, mm, CloneAttributes(dd.Attributes), dd.IsRefining, dd.ParentTraits.ConvertAll(CloneType)); } else if (d is ModuleDecl) { if (d is LiteralModuleDecl moduleDecl) { @@ -187,14 +188,14 @@ public TypeParameter.TypeParameterCharacteristics CloneTPChar( } public DatatypeCtor CloneCtor(DatatypeCtor ct) { - return new DatatypeCtor(Range(ct.RangeToken), ct.NameNode.Clone(this), ct.IsGhost, + return new DatatypeCtor(Origin(ct.Origin), ct.NameNode.Clone(this), ct.IsGhost, ct.Formals.ConvertAll(bv => CloneFormal(bv, false)), CloneAttributes(ct.Attributes)); } public TypeParameter CloneTypeParam(TypeParameter tp) { return (TypeParameter)typeParameterClones.GetOrCreate(tp, - () => new TypeParameter(Range(tp.RangeToken), tp.NameNode.Clone(this), tp.VarianceSyntax, - CloneTPChar(tp.Characteristics))); + () => new TypeParameter(Origin(tp.Origin), tp.NameNode.Clone(this), tp.VarianceSyntax, + CloneTPChar(tp.Characteristics), tp.TypeBounds.ConvertAll(CloneType))); } public virtual MemberDecl CloneMember(MemberDecl member, bool isReference) { @@ -232,12 +233,11 @@ public virtual Type CloneType(Type t) { } else if (t is MultiSetType) { var tt = (MultiSetType)t; return new MultiSetType(tt.HasTypeArg() ? CloneType(tt.Arg) : null); - } else if (t is MapType) { - var tt = (MapType)t; - return new MapType(tt.Finite, CloneType(tt.Domain), CloneType(tt.Range)); + } else if (t is MapType mapType) { + return new MapType(this, mapType); } else if (t is ArrowType) { var tt = (ArrowType)t; - return new ArrowType(Tok(tt.tok), tt.Args.ConvertAll(CloneType), CloneType(tt.Result)); + return new ArrowType(Origin(tt.Origin), tt.Args.ConvertAll(CloneType), CloneType(tt.Result)); } else if (t is UserDefinedType) { var tt = (UserDefinedType)t; #if TEST_TYPE_SYNONYM_TRANSPARENCY @@ -257,6 +257,9 @@ public virtual Type CloneType(Type t) { return inferredTypeProxy; } else if (t is ParamTypeProxy) { return new ParamTypeProxy(CloneTypeParam(((ParamTypeProxy)t).orig)); + } else if (t is TypeRefinementWrapper typeRefinementWrapper) { + // don't bother keeping TypeRefinementWrapper wrappers + return CloneType(typeRefinementWrapper.T); } else { Contract.Assert(false); // unexpected type (e.g., no other type proxies are expected at this time) return null; // to please compiler @@ -266,9 +269,9 @@ public virtual Type CloneType(Type t) { public virtual Formal CloneFormal(Formal formal, bool isReference) { return (Formal)clones.GetOrCreate(formal, () => isReference ? formal - : new Formal(Tok(formal.tok), formal.Name, CloneType(formal.Type), formal.InParam, formal.IsGhost, - CloneExpr(formal.DefaultValue), formal.IsOld, formal.IsNameOnly, formal.IsOlder, formal.NameForCompilation) { - RangeToken = formal.RangeToken, + : new Formal(Origin(formal.Origin), new Name(this, formal.NameNode), CloneType(formal.Type), formal.InParam, formal.IsGhost, + CloneExpr(formal.DefaultValue), CloneAttributes(formal.Attributes), + formal.IsOld, formal.IsNameOnly, formal.IsOlder, formal.NameForCompilation) { IsTypeExplicit = formal.IsTypeExplicit }); } @@ -279,7 +282,7 @@ public virtual BoundVar CloneBoundVar(BoundVar bv, bool isReference) { return bv; } - var bvNew = new BoundVar(Tok(bv.tok), bv.Name, CloneType(bv.SyntacticType)); + var bvNew = new BoundVar(Origin(bv.Origin), new Name(this, bv.NameNode), CloneType(bv.SyntacticType)); bvNew.IsGhost = bv.IsGhost; return bvNew; }); @@ -330,25 +333,35 @@ public Attributes CloneAttributes(Attributes attrs) { } else if (!CloneResolvedFields && attrs.Name.StartsWith("_")) { // skip this attribute, since it would have been produced during resolution return CloneAttributes(attrs.Prev); - } else if (attrs is UserSuppliedAttributes) { - var usa = (UserSuppliedAttributes)attrs; - return new UserSuppliedAttributes(Tok(usa.tok), Tok(usa.OpenBrace), Tok(usa.CloseBrace), + } else if (attrs is UserSuppliedAttributes usa) { + return new UserSuppliedAttributes(Origin(usa.Origin), Origin(usa.OpenBrace), Origin(usa.CloseBrace), attrs.Args.ConvertAll(CloneExpr), CloneAttributes(attrs.Prev)); + } else if (attrs is UserSuppliedAtAttribute usaa) { + var arg = CloneExpr(usaa.Arg); + if (usaa.Arg.Type != null) { // The attribute has already been expanded + arg.Type = usaa.Arg.Type; + arg.PreType = usaa.Arg.PreType; + } + return new UserSuppliedAtAttribute(Origin(usaa.Origin), arg, CloneAttributes(usaa.Prev)) { + Builtin = usaa.Builtin + }; } else { - return new Attributes(attrs.Name, attrs.Args.ConvertAll(CloneExpr), CloneAttributes(attrs.Prev)); + var result = new Attributes(attrs.Name, attrs.Args.ConvertAll(CloneExpr), CloneAttributes(attrs.Prev)); + result.SetOrigin(Origin(attrs.Origin)); + return result; } } public AttributedExpression CloneAttributedExpr(AttributedExpression expr) { var mfe = new AttributedExpression(CloneExpr(expr.E), - expr.Label == null ? null : new AssertLabel(Tok(expr.Label.Tok), expr.Label.Name), + expr.Label == null ? null : new AssertLabel(Origin(expr.Label.Tok), expr.Label.Name), CloneAttributes(expr.Attributes)); mfe.Attributes = CloneAttributes(expr.Attributes); return mfe; } public virtual ActualBinding CloneActualBinding(ActualBinding binding) { - return new ActualBinding(binding.FormalParameterName == null ? null : Tok(binding.FormalParameterName), + return new ActualBinding(binding.FormalParameterName == null ? null : Origin(binding.FormalParameterName), CloneExpr(binding.Actual)); } @@ -367,13 +380,13 @@ public virtual Expression CloneExpr(Expression expr) { public MatchCaseExpr CloneMatchCaseExpr(MatchCaseExpr c) { Contract.Requires(c != null); Contract.Requires(c.Arguments != null); - return new MatchCaseExpr(Tok(c.tok), c.Ctor, c.FromBoundVar, + return new MatchCaseExpr(Origin(c.Origin), c.Ctor, c.FromBoundVar, c.Arguments.ConvertAll(bv => CloneBoundVar(bv, false)), CloneExpr(c.Body), CloneAttributes(c.Attributes)); } public NestedMatchCaseExpr CloneNestedMatchCaseExpr(NestedMatchCaseExpr c) { Contract.Requires(c != null); - return new NestedMatchCaseExpr(Tok(c.Tok), CloneExtendedPattern(c.Pat), CloneExpr(c.Body), + return new NestedMatchCaseExpr(Origin(c.Origin), CloneExtendedPattern(c.Pat), CloneExpr(c.Body), CloneAttributes(c.Attributes)); } @@ -401,7 +414,7 @@ public virtual BlockStmt CloneBlockStmt(BlockStmt stmt) { if (stmt == null) { return null; } else { - return new BlockStmt(Tok(stmt.RangeToken), stmt.Body.ConvertAll(CloneStmt)); + return new BlockStmt(Origin(stmt.Origin), stmt.Body.ConvertAll(stmt1 => CloneStmt(stmt1, false))); } } @@ -409,20 +422,25 @@ public virtual DividedBlockStmt CloneDividedBlockStmt(DividedBlockStmt stmt) { if (stmt == null) { return null; } else { - return new DividedBlockStmt(Tok(stmt.RangeToken), stmt.BodyInit.ConvertAll(CloneStmt), - stmt.SeparatorTok == null ? null : Tok(stmt.SeparatorTok), stmt.BodyProper.ConvertAll(CloneStmt)); + return new DividedBlockStmt(Origin(stmt.Origin), stmt.BodyInit.ConvertAll(stmt1 => CloneStmt(stmt1, false)), + stmt.SeparatorTok == null ? null : Origin(stmt.SeparatorTok), stmt.BodyProper.ConvertAll(stmt1 => CloneStmt(stmt1, false))); } } - public virtual Statement CloneStmt(Statement stmt) { + public virtual Statement CloneStmt(Statement stmt, bool isReference) { if (stmt == null) { return null; } + if (statementClones.TryGetValue(stmt, out var cachedResult)) { return cachedResult; } + if (isReference) { + return stmt; + } + if (stmt is ICloneable cloneable) { var r = cloneable.Clone(this); // add labels to the cloned statement @@ -439,19 +457,19 @@ public virtual Statement CloneStmt(Statement stmt) { public MatchCaseStmt CloneMatchCaseStmt(MatchCaseStmt c) { Contract.Requires(c != null); Contract.Assert(c.Arguments != null); - return new MatchCaseStmt(Tok(c.RangeToken), c.Ctor, c.FromBoundVar, + return new MatchCaseStmt(Origin(c.Origin), c.Ctor, c.FromBoundVar, c.Arguments.ConvertAll(v => CloneBoundVar(v, false)), - c.Body.ConvertAll(CloneStmt), CloneAttributes(c.Attributes)); + c.Body.ConvertAll(stmt => CloneStmt(stmt, false)), CloneAttributes(c.Attributes)); } public ExtendedPattern CloneExtendedPattern(ExtendedPattern pat) { switch (pat) { case LitPattern p: - return new LitPattern(p.Tok, CloneExpr(p.OrigLit)); + return new LitPattern(p.Origin, CloneExpr(p.OrigLit)); case IdPattern p: return new IdPattern(this, p); case DisjunctivePattern p: - return new DisjunctivePattern(p.Tok, p.Alternatives.ConvertAll(CloneExtendedPattern), p.IsGhost); + return new DisjunctivePattern(p.Origin, p.Alternatives.ConvertAll(CloneExtendedPattern), p.IsGhost); default: Contract.Assert(false); return null; @@ -460,7 +478,7 @@ public ExtendedPattern CloneExtendedPattern(ExtendedPattern pat) { public NestedMatchCaseStmt CloneNestedMatchCaseStmt(NestedMatchCaseStmt c) { Contract.Requires(c != null); - return new NestedMatchCaseStmt(c.Tok, CloneExtendedPattern(c.Pat), c.Body.ConvertAll(CloneStmt), + return new NestedMatchCaseStmt(c.Origin, CloneExtendedPattern(c.Pat), c.Body.ConvertAll(stmt => CloneStmt(stmt, false)), CloneAttributes(c.Attributes)); } @@ -483,36 +501,36 @@ public void AddStmtLabels(Statement s, LList public class ApplySuffix : SuffixExpr, ICloneable, ICanFormat { - public readonly IToken/*?*/ AtTok; - public readonly IToken CloseParen; + public readonly IOrigin/*?*/ AtTok; + public readonly Token CloseParen; public readonly ActualBindings Bindings; public List Args => Bindings.Arguments; + [FilledInDuringResolution] public MethodCallInformation MethodCallInfo = null; // resolution will set to a non-null value if ApplySuffix makes a method call public override IEnumerable Children => ResolvedExpression == null ? base.Children.Concat(Bindings == null ? new List() : Args ?? Enumerable.Empty()) : new[] { ResolvedExpression }; @@ -28,15 +29,15 @@ public ApplySuffix Clone(Cloner cloner) { public ApplySuffix(Cloner cloner, ApplySuffix original) : base(cloner, original) { - AtTok = original.AtTok == null ? null : cloner.Tok(original.AtTok); - CloseParen = cloner.Tok(original.CloseParen); + AtTok = original.AtTok == null ? null : cloner.Origin(original.AtTok); + CloseParen = original.CloseParen; FormatTokens = original.FormatTokens; Bindings = new ActualBindings(cloner, original.Bindings); } - public ApplySuffix(IToken tok, IToken/*?*/ atLabel, Expression lhs, List args, IToken closeParen) - : base(tok, lhs) { - Contract.Requires(tok != null); + public ApplySuffix(IOrigin origin, IOrigin/*?*/ atLabel, Expression lhs, List args, Token closeParen) + : base(origin, lhs) { + Contract.Requires(origin != null); Contract.Requires(lhs != null); Contract.Requires(cce.NonNullElements(args)); AtTok = atLabel; @@ -65,10 +66,10 @@ public override IEnumerable PreResolveSubExpressions { /// The name of the target function or method. /// The arguments to apply the function or method to. /// - public static Expression MakeRawApplySuffix(IToken tok, string name, List args) { + public static Expression MakeRawApplySuffix(IOrigin tok, string name, List args) { var nameExpr = new NameSegment(tok, name, null); var argBindings = args.ConvertAll(arg => new ActualBinding(null, arg)); - return new ApplySuffix(tok, null, nameExpr, argBindings, tok); + return new ApplySuffix(tok, null, nameExpr, argBindings, Token.NoToken); } public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { diff --git a/Source/DafnyCore/AST/Expressions/Applications/ExprDotName.cs b/Source/DafnyCore/AST/Expressions/Applications/ExprDotName.cs index 4c24490cdd1..3adc0cee290 100644 --- a/Source/DafnyCore/AST/Expressions/Applications/ExprDotName.cs +++ b/Source/DafnyCore/AST/Expressions/Applications/ExprDotName.cs @@ -7,7 +7,8 @@ namespace Microsoft.Dafny; /// An ExprDotName desugars into either an IdentifierExpr (if the Lhs is a static name) or a MemberSelectExpr (if the Lhs is a computed expression). /// public class ExprDotName : SuffixExpr, ICloneable { - public readonly string SuffixName; + public readonly Name SuffixNameNode; + public string SuffixName => SuffixNameNode.Value; public readonly List OptTypeArguments; /// @@ -28,16 +29,16 @@ public ExprDotName Clone(Cloner cloner) { } public ExprDotName(Cloner cloner, ExprDotName original) : base(cloner, original) { - SuffixName = original.SuffixName; + SuffixNameNode = new Name(cloner, original.SuffixNameNode); OptTypeArguments = original.OptTypeArguments?.ConvertAll(cloner.CloneType); } - public ExprDotName(IToken tok, Expression obj, string suffixName, List optTypeArguments) - : base(tok, obj) { - Contract.Requires(tok != null); + public ExprDotName(IOrigin origin, Expression obj, Name suffixName, List optTypeArguments) + : base(origin, obj) { + Contract.Requires(origin != null); Contract.Requires(obj != null); Contract.Requires(suffixName != null); - this.SuffixName = suffixName; + this.SuffixNameNode = suffixName; OptTypeArguments = optTypeArguments; } } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/Applications/FunctionCallExpr.cs b/Source/DafnyCore/AST/Expressions/Applications/FunctionCallExpr.cs index b612767be97..75b5c6e5bcd 100644 --- a/Source/DafnyCore/AST/Expressions/Applications/FunctionCallExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Applications/FunctionCallExpr.cs @@ -4,11 +4,12 @@ namespace Microsoft.Dafny; -public class FunctionCallExpr : Expression, IHasUsages, ICloneable { - public string Name; +public class FunctionCallExpr : Expression, IHasReferences, ICloneable { + public Name NameNode; + public string Name => NameNode.Value; public readonly Expression Receiver; - public readonly IToken OpenParen; // can be null if Args.Count == 0 - public readonly IToken CloseParen; + public readonly IOrigin OpenParen; // can be null if Args.Count == 0 + public readonly Token CloseParen; public readonly Label/*?*/ AtLabel; public readonly ActualBindings Bindings; public List Args => Bindings.Arguments; @@ -23,10 +24,7 @@ public class FunctionCallExpr : Expression, IHasUsages, ICloneable public Dictionary GetTypeArgumentSubstitutions() { - var typeMap = new Dictionary(); - Util.AddToDict(typeMap, Function.EnclosingClass.TypeArgs, TypeApplication_AtEnclosingClass); - Util.AddToDict(typeMap, Function.TypeArgs, TypeApplication_JustFunction); - return typeMap; + return TypeArgumentSubstitutionsWithParents(); } /// @@ -77,9 +75,15 @@ void ObjectInvariant() { [FilledInDuringResolution] public Function Function; - public FunctionCallExpr(IToken tok, string fn, Expression receiver, IToken openParen, IToken closeParen, [Captured] List args, Label/*?*/ atLabel = null) - : this(tok, fn, receiver, openParen, closeParen, new ActualBindings(args), atLabel) { - Contract.Requires(tok != null); + public FunctionCallExpr(string fn, Expression receiver, IOrigin openParen, Token closeParen, + [Captured] ActualBindings bindings, Label /*?*/ atLabel = null) + : this(Token.NoToken, new Name(Token.NoToken, fn), receiver, openParen, closeParen, bindings, + atLabel) { + } + + public FunctionCallExpr(IOrigin origin, Name fn, Expression receiver, IOrigin openParen, Token closeParen, [Captured] List args, Label/*?*/ atLabel = null) + : this(origin, fn, receiver, openParen, closeParen, new ActualBindings(args), atLabel) { + Contract.Requires(origin != null); Contract.Requires(fn != null); Contract.Requires(receiver != null); Contract.Requires(cce.NonNullElements(args)); @@ -87,16 +91,16 @@ public FunctionCallExpr(IToken tok, string fn, Expression receiver, IToken openP Contract.Ensures(type == null); } - public FunctionCallExpr(IToken tok, string fn, Expression receiver, IToken openParen, IToken closeParen, [Captured] ActualBindings bindings, Label/*?*/ atLabel = null) - : base(tok) { - Contract.Requires(tok != null); + public FunctionCallExpr(IOrigin origin, Name fn, Expression receiver, IOrigin openParen, Token closeParen, [Captured] ActualBindings bindings, Label/*?*/ atLabel = null) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(fn != null); Contract.Requires(receiver != null); Contract.Requires(bindings != null); Contract.Requires(openParen != null); Contract.Ensures(type == null); - this.Name = fn; + this.NameNode = fn; this.Receiver = receiver; this.OpenParen = openParen; this.CloseParen = closeParen; @@ -109,9 +113,9 @@ public FunctionCallExpr(IToken tok, string fn, Expression receiver, IToken openP /// This constructor is intended to be used when constructing a resolved FunctionCallExpr. The "args" are expected /// to be already resolved, and are all given positionally. /// - public FunctionCallExpr(IToken tok, string fn, Expression receiver, IToken openParen, IToken closeParen, [Captured] List args, + public FunctionCallExpr(IOrigin origin, Name fn, Expression receiver, IOrigin openParen, Token closeParen, [Captured] List args, Label /*?*/ atLabel = null) - : this(tok, fn, receiver, openParen, closeParen, args.ConvertAll(e => new ActualBinding(null, e)), atLabel) { + : this(origin, fn, receiver, openParen, closeParen, args.ConvertAll(e => new ActualBinding(null, e)), atLabel) { Bindings.AcceptArgumentExpressionsAsExactParameterList(); } @@ -120,10 +124,10 @@ public FunctionCallExpr Clone(Cloner cloner) { } public FunctionCallExpr(Cloner cloner, FunctionCallExpr original) : base(cloner, original) { - Name = original.Name; + NameNode = new Name(cloner, original.NameNode); Receiver = cloner.CloneExpr(original.Receiver); - OpenParen = original.OpenParen == null ? null : cloner.Tok(original.OpenParen); - CloseParen = original.CloseParen == null ? null : cloner.Tok(original.CloseParen); + OpenParen = original.OpenParen == null ? null : cloner.Origin(original.OpenParen); + CloseParen = original.CloseParen; Bindings = new ActualBindings(cloner, original.Bindings); AtLabel = original.AtLabel; @@ -147,9 +151,7 @@ public override IEnumerable SubExpressions { } public override IEnumerable ComponentTypes => Util.Concat(TypeApplication_AtEnclosingClass, TypeApplication_JustFunction); - public IEnumerable GetResolvedDeclarations() { - return Enumerable.Repeat(Function, 1); + public IEnumerable GetReferences() { + return Enumerable.Repeat(new Reference(NameNode.Origin, Function), 1); } - - public IToken NameToken => tok; } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/Applications/MemberSelectExpr.cs b/Source/DafnyCore/AST/Expressions/Applications/MemberSelectExpr.cs index b8b27eb640b..c3aae1fcbac 100644 --- a/Source/DafnyCore/AST/Expressions/Applications/MemberSelectExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Applications/MemberSelectExpr.cs @@ -4,9 +4,10 @@ namespace Microsoft.Dafny; -public class MemberSelectExpr : Expression, IHasUsages, ICloneable { +public class MemberSelectExpr : Expression, IHasReferences, ICloneable { public readonly Expression Obj; - public string MemberName; + public Name MemberNameNode; + public string MemberName => MemberNameNode.Value; [FilledInDuringResolution] public MemberDecl Member; // will be a Field or Function [FilledInDuringResolution] public Label /*?*/ AtLabel; // non-null for a two-state selection [FilledInDuringResolution] public bool InCompiledContext; @@ -15,25 +16,25 @@ public class MemberSelectExpr : Expression, IHasUsages, ICloneable - [FilledInDuringResolution] public List PreTypeApplication_AtEnclosingClass; + [FilledInDuringResolution] public List PreTypeApplicationAtEnclosingClass; /// /// PreTypeApplication_JustMember is the list of type arguments used to instantiate the type parameters /// of Member. /// - [FilledInDuringResolution] public List PreTypeApplication_JustMember; + [FilledInDuringResolution] public List PreTypeApplicationJustMember; /// /// TypeApplication_AtEnclosingClass is the list of type arguments used to instantiate the type that /// declares Member (which is some supertype of the receiver type). /// - [FilledInDuringResolution] public List TypeApplication_AtEnclosingClass; + [FilledInDuringResolution] public List TypeApplicationAtEnclosingClass; /// /// TypeApplication_JustMember is the list of type arguments used to instantiate the type parameters /// of Member. /// - [FilledInDuringResolution] public List TypeApplication_JustMember; + [FilledInDuringResolution] public List TypeApplicationJustMember; /// /// Returns a mapping from formal type parameters to actual type arguments. For example, given @@ -54,21 +55,21 @@ public Dictionary TypeArgumentSubstitutionsAtMemberDeclarat // Add the mappings from the member's own type parameters if (Member is ICallable icallable) { - Contract.Assert(TypeApplication_JustMember.Count == icallable.TypeArgs.Count); + Contract.Assert(TypeApplicationJustMember.Count == icallable.TypeArgs.Count); for (var i = 0; i < icallable.TypeArgs.Count; i++) { - subst.Add(icallable.TypeArgs[i], TypeApplication_JustMember[i]); + subst.Add(icallable.TypeArgs[i], TypeApplicationJustMember[i]); } } else { - Contract.Assert(TypeApplication_JustMember.Count == 0); + Contract.Assert(TypeApplicationJustMember.Count == 0); } // Add the mappings from the enclosing class. TopLevelDecl cl = Member.EnclosingClass; // Expand the type down to its non-null type, if any if (cl != null) { - Contract.Assert(cl.TypeArgs.Count == TypeApplication_AtEnclosingClass.Count); + Contract.Assert(cl.TypeArgs.Count == TypeApplicationAtEnclosingClass.Count); for (var i = 0; i < cl.TypeArgs.Count; i++) { - subst.Add(cl.TypeArgs[i], TypeApplication_AtEnclosingClass[i]); + subst.Add(cl.TypeArgs[i], TypeApplicationAtEnclosingClass[i]); } } @@ -94,21 +95,21 @@ public Dictionary PreTypeArgumentSubstitutionsAtMemberDe // Add the mappings from the member's own type parameters if (Member is ICallable icallable) { - Contract.Assert(PreTypeApplication_JustMember.Count == icallable.TypeArgs.Count); + Contract.Assert(PreTypeApplicationJustMember.Count == icallable.TypeArgs.Count); for (var i = 0; i < icallable.TypeArgs.Count; i++) { - subst.Add(icallable.TypeArgs[i], PreTypeApplication_JustMember[i]); + subst.Add(icallable.TypeArgs[i], PreTypeApplicationJustMember[i]); } } else { - Contract.Assert(PreTypeApplication_JustMember.Count == 0); + Contract.Assert(PreTypeApplicationJustMember.Count == 0); } // Add the mappings from the enclosing class. TopLevelDecl cl = Member.EnclosingClass; // Expand the type down to its non-null type, if any if (cl != null) { - Contract.Assert(cl.TypeArgs.Count == PreTypeApplication_AtEnclosingClass.Count); + Contract.Assert(cl.TypeArgs.Count == PreTypeApplicationAtEnclosingClass.Count); for (var i = 0; i < cl.TypeArgs.Count; i++) { - subst.Add(cl.TypeArgs[i], PreTypeApplication_AtEnclosingClass[i]); + subst.Add(cl.TypeArgs[i], PreTypeApplicationAtEnclosingClass[i]); } } @@ -134,7 +135,7 @@ public Dictionary TypeArgumentSubstitutionsWithParents() { Contract.Requires(WasResolved()); Contract.Ensures(Contract.Result>() != null); - return TypeArgumentSubstitutionsWithParentsAux(Obj.Type, Member, TypeApplication_JustMember); + return TypeArgumentSubstitutionsWithParentsAux(Obj.Type, Member, TypeApplicationJustMember); } public static Dictionary TypeArgumentSubstitutionsWithParentsAux(Type receiverType, MemberDecl member, List typeApplicationMember) { @@ -193,8 +194,8 @@ public static Dictionary TypeArgumentSubstitutionsWithParen void ObjectInvariant() { Contract.Invariant(Obj != null); Contract.Invariant(MemberName != null); - Contract.Invariant((Member != null) == (TypeApplication_AtEnclosingClass != null)); // TypeApplication_* are set whenever Member is set - Contract.Invariant((Member != null) == (TypeApplication_JustMember != null)); // TypeApplication_* are set whenever Member is set + Contract.Invariant((Member != null) == (TypeApplicationAtEnclosingClass != null)); // TypeApplication_* are set whenever Member is set + Contract.Invariant((Member != null) == (TypeApplicationJustMember != null)); // TypeApplication_* are set whenever Member is set } public MemberSelectExpr Clone(Cloner cloner) { @@ -203,32 +204,32 @@ public MemberSelectExpr Clone(Cloner cloner) { public MemberSelectExpr(Cloner cloner, MemberSelectExpr original) : base(cloner, original) { Obj = cloner.CloneExpr(original.Obj); - MemberName = original.MemberName; + MemberNameNode = new Name(cloner, original.MemberNameNode); if (cloner.CloneResolvedFields) { Member = cloner.CloneMember(original.Member, true); AtLabel = original.AtLabel; InCompiledContext = original.InCompiledContext; - TypeApplication_AtEnclosingClass = original.TypeApplication_AtEnclosingClass; - TypeApplication_JustMember = original.TypeApplication_JustMember; + TypeApplicationAtEnclosingClass = original.TypeApplicationAtEnclosingClass; + TypeApplicationJustMember = original.TypeApplicationJustMember; } } - public MemberSelectExpr(IToken tok, Expression obj, string memberName) - : base(tok) { - Contract.Requires(tok != null); + public MemberSelectExpr(IOrigin origin, Expression obj, Name memberName) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(obj != null); Contract.Requires(memberName != null); this.Obj = obj; - this.MemberName = memberName; + this.MemberNameNode = memberName; } /// /// Returns a resolved MemberSelectExpr for a field. /// - public MemberSelectExpr(IToken tok, Expression obj, Field field) - : this(tok, obj, field.Name) { - Contract.Requires(tok != null); + public MemberSelectExpr(IOrigin origin, Expression obj, Field field) + : this(origin, obj, new Name(field.Name)) { + Contract.Requires(origin != null); Contract.Requires(obj != null); Contract.Requires(field != null); Contract.Requires(obj.Type != null); // "obj" is required to be resolved @@ -236,17 +237,16 @@ public MemberSelectExpr(IToken tok, Expression obj, Field field) this.Member = field; // resolve here var receiverType = obj.Type.NormalizeExpand(); - this.TypeApplication_AtEnclosingClass = receiverType.TypeArgs; - this.TypeApplication_JustMember = new List(); - this.ResolvedOutparameterTypes = new List(); + this.TypeApplicationAtEnclosingClass = receiverType.TypeArgs; + this.TypeApplicationJustMember = new List(); var typeMap = new Dictionary(); if (receiverType is UserDefinedType udt) { var cl = udt.ResolvedClass as TopLevelDeclWithMembers; Contract.Assert(cl != null); - Contract.Assert(cl.TypeArgs.Count == TypeApplication_AtEnclosingClass.Count); + Contract.Assert(cl.TypeArgs.Count == TypeApplicationAtEnclosingClass.Count); for (var i = 0; i < cl.TypeArgs.Count; i++) { - typeMap.Add(cl.TypeArgs[i], TypeApplication_AtEnclosingClass[i]); + typeMap.Add(cl.TypeArgs[i], TypeApplicationAtEnclosingClass[i]); } foreach (var entry in cl.ParentFormalTypeParametersToActuals) { var v = entry.Value.Subst(typeMap); @@ -255,9 +255,9 @@ public MemberSelectExpr(IToken tok, Expression obj, Field field) } else if (field.EnclosingClass == null) { // leave typeMap as the empty substitution } else { - Contract.Assert(field.EnclosingClass.TypeArgs.Count == TypeApplication_AtEnclosingClass.Count); + Contract.Assert(field.EnclosingClass.TypeArgs.Count == TypeApplicationAtEnclosingClass.Count); for (var i = 0; i < field.EnclosingClass.TypeArgs.Count; i++) { - typeMap.Add(field.EnclosingClass.TypeArgs[i], TypeApplication_AtEnclosingClass[i]); + typeMap.Add(field.EnclosingClass.TypeArgs[i], TypeApplicationAtEnclosingClass[i]); } } this.Type = field.Type.Subst(typeMap); // resolve here @@ -290,13 +290,11 @@ public override IEnumerable SubExpressions { get { yield return Obj; } } - public override IEnumerable ComponentTypes => Util.Concat(TypeApplication_AtEnclosingClass, TypeApplication_JustMember); + public override IEnumerable ComponentTypes => Util.Concat(TypeApplicationAtEnclosingClass, TypeApplicationJustMember); [FilledInDuringResolution] public List ResolvedOutparameterTypes; - public IEnumerable GetResolvedDeclarations() { - return new[] { Member }; + public IEnumerable GetReferences() { + return new[] { new Reference(Center, Member) }; } - - public IToken NameToken => tok; -} \ No newline at end of file +} diff --git a/Source/DafnyCore/AST/Expressions/Applications/MultiSelectExpr.cs b/Source/DafnyCore/AST/Expressions/Applications/MultiSelectExpr.cs index 1a1d5270f6b..edabd5ff48b 100644 --- a/Source/DafnyCore/AST/Expressions/Applications/MultiSelectExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Applications/MultiSelectExpr.cs @@ -19,9 +19,9 @@ public MultiSelectExpr(Cloner cloner, MultiSelectExpr original) : base(cloner, o Array = cloner.CloneExpr(original.Array); } - public MultiSelectExpr(IToken tok, Expression array, List indices) - : base(tok) { - Contract.Requires(tok != null); + public MultiSelectExpr(IOrigin origin, Expression array, List indices) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(array != null); Contract.Requires(cce.NonNullElements(indices) && 1 <= indices.Count); diff --git a/Source/DafnyCore/AST/Expressions/Applications/NameSegment.cs b/Source/DafnyCore/AST/Expressions/Applications/NameSegment.cs index e99628ddadc..8b803a02863 100644 --- a/Source/DafnyCore/AST/Expressions/Applications/NameSegment.cs +++ b/Source/DafnyCore/AST/Expressions/Applications/NameSegment.cs @@ -6,10 +6,11 @@ namespace Microsoft.Dafny; public class NameSegment : ConcreteSyntaxExpression, ICloneable, ICanFormat { public readonly string Name; + public Name NameNode => new Name(Origin, Name); public readonly List OptTypeArguments; - public NameSegment(IToken tok, string name, List optTypeArguments) - : base(tok) { - Contract.Requires(tok != null); + public NameSegment(IOrigin origin, string name, List optTypeArguments) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(name != null); Contract.Requires(optTypeArguments == null || optTypeArguments.Count > 0); Name = name; diff --git a/Source/DafnyCore/AST/Expressions/Applications/SeqSelectExpr.cs b/Source/DafnyCore/AST/Expressions/Applications/SeqSelectExpr.cs index 5c629bc582a..0aa0d2eb5d1 100644 --- a/Source/DafnyCore/AST/Expressions/Applications/SeqSelectExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Applications/SeqSelectExpr.cs @@ -8,14 +8,14 @@ public class SeqSelectExpr : Expression, ICloneable { public readonly Expression Seq; public readonly Expression E0; public readonly Expression E1; - public readonly IToken CloseParen; + public readonly Token CloseParen; public SeqSelectExpr(Cloner cloner, SeqSelectExpr original) : base(cloner, original) { SelectOne = original.SelectOne; Seq = cloner.CloneExpr(original.Seq); E0 = cloner.CloneExpr(original.E0); E1 = cloner.CloneExpr(original.E1); - CloseParen = cloner.Tok(original.CloseParen); + CloseParen = original.CloseParen; } [ContractInvariantMethod] @@ -24,9 +24,9 @@ void ObjectInvariant() { Contract.Invariant(!SelectOne || E1 == null); } - public SeqSelectExpr(IToken tok, bool selectOne, Expression seq, Expression e0, Expression e1, IToken closeParen) - : base(tok) { - Contract.Requires(tok != null); + public SeqSelectExpr(IOrigin origin, bool selectOne, Expression seq, Expression e0, Expression e1, Token closeParen) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(seq != null); Contract.Requires(!selectOne || e1 == null); diff --git a/Source/DafnyCore/AST/Expressions/Applications/StaticReceiverExpr.cs b/Source/DafnyCore/AST/Expressions/Applications/StaticReceiverExpr.cs index 705193fbaf0..9a14dc07319 100644 --- a/Source/DafnyCore/AST/Expressions/Applications/StaticReceiverExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Applications/StaticReceiverExpr.cs @@ -21,9 +21,9 @@ public class StaticReceiverExpr : LiteralExpr, ICloneable { /// public Expression ContainerExpression; - public StaticReceiverExpr(IToken tok, Type t, bool isImplicit) - : base(tok) { - Contract.Requires(tok != null); + public StaticReceiverExpr(IOrigin origin, Type t, bool isImplicit) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(t != null); UnresolvedType = t; IsImplicit = isImplicit; @@ -38,12 +38,12 @@ public StaticReceiverExpr(Cloner cloner, StaticReceiverExpr original) : base(clo /// Constructs a resolved LiteralExpr representing the fictitious static-receiver literal whose type is /// "cl" parameterized by the type arguments of "cl" itself. /// - public StaticReceiverExpr(IToken tok, TopLevelDeclWithMembers cl, bool isImplicit, Expression lhs = null) - : base(tok) { - Contract.Requires(tok != null); + public StaticReceiverExpr(IOrigin origin, TopLevelDeclWithMembers cl, bool isImplicit, Expression lhs = null) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(cl != null); var typeArgs = cl.TypeArgs.ConvertAll(tp => (Type)new UserDefinedType(tp)); - Type = new UserDefinedType(tok, cl is DefaultClassDecl ? cl.Name : cl.Name + "?", cl, typeArgs); + Type = new UserDefinedType(origin, cl is DefaultClassDecl ? cl.Name : cl.Name + "?", cl, typeArgs); UnresolvedType = Type; IsImplicit = isImplicit; ObjectToDiscard = lhs; @@ -62,9 +62,9 @@ public StaticReceiverExpr(IToken tok, TopLevelDeclWithMembers cl, bool isImplici /// a trait that in turn extends trait "W(g(Y))". If "t" denotes type "C(G)" and "cl" denotes "W", /// then type of the StaticReceiverExpr will be "T(g(f(G)))". /// - public StaticReceiverExpr(IToken tok, UserDefinedType t, TopLevelDeclWithMembers cl, bool isImplicit, Expression lhs = null) - : base(tok) { - Contract.Requires(tok != null); + public StaticReceiverExpr(IOrigin origin, UserDefinedType t, TopLevelDeclWithMembers cl, bool isImplicit, Expression lhs = null) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(t.ResolvedClass != null); Contract.Requires(cl != null); var top = t.AsTopLevelTypeWithMembersBypassInternalSynonym; @@ -73,9 +73,9 @@ public StaticReceiverExpr(IToken tok, UserDefinedType t, TopLevelDeclWithMembers var clArgsInTermsOfTFormals = cl.TypeArgs.ConvertAll(tp => top.ParentFormalTypeParametersToActuals[tp]); var subst = TypeParameter.SubstitutionMap(top.TypeArgs, t.TypeArgs); var typeArgs = clArgsInTermsOfTFormals.ConvertAll(ty => ty.Subst(subst)); - Type = new UserDefinedType(tok, cl.Name, cl, typeArgs); + Type = new UserDefinedType(origin, cl.Name, cl, typeArgs); } else if (t.Name != cl.Name) { // t may be using the name "C?", and we'd prefer it read "C" - Type = new UserDefinedType(tok, cl.Name, cl, t.TypeArgs); + Type = new UserDefinedType(origin, cl.Name, cl, t.TypeArgs); } else { Type = t; } diff --git a/Source/DafnyCore/AST/Expressions/Applications/SuffixExpr.cs b/Source/DafnyCore/AST/Expressions/Applications/SuffixExpr.cs index 7deb3808106..42e11a89e18 100644 --- a/Source/DafnyCore/AST/Expressions/Applications/SuffixExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Applications/SuffixExpr.cs @@ -42,9 +42,9 @@ protected SuffixExpr(Cloner cloner, SuffixExpr original) : base(cloner, original Lhs = cloner.CloneExpr(original.Lhs); } - public SuffixExpr(IToken tok, Expression lhs) - : base(tok) { - Contract.Requires(tok != null); + public SuffixExpr(IOrigin origin, Expression lhs) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(lhs != null); Lhs = lhs; } diff --git a/Source/DafnyCore/AST/Expressions/Applications/ThisExpr.cs b/Source/DafnyCore/AST/Expressions/Applications/ThisExpr.cs index fadf6162007..47b8c197898 100644 --- a/Source/DafnyCore/AST/Expressions/Applications/ThisExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Applications/ThisExpr.cs @@ -6,9 +6,9 @@ public class ThisExpr : Expression, ICloneable { public ThisExpr(Cloner cloner, ThisExpr original) : base(cloner, original) { } - public ThisExpr(IToken tok) - : base(tok) { - Contract.Requires(tok != null); + public ThisExpr(IOrigin origin) + : base(origin) { + Contract.Requires(origin != null); } /// @@ -17,12 +17,12 @@ public ThisExpr(IToken tok) /// to obtain a Dafny "this" expression. /// public ThisExpr(MemberDecl m) - : base(m.tok) { + : base(m.Origin) { Contract.Requires(m != null); - Contract.Requires(m.tok != null); + Contract.Requires(m.Origin != null); Contract.Requires(m.EnclosingClass != null); Contract.Requires(!m.IsStatic); - Type = ModuleResolver.GetReceiverType(m.tok, m); + Type = ModuleResolver.GetReceiverType(m.Origin, m); } /// @@ -31,10 +31,10 @@ public ThisExpr(MemberDecl m) /// to obtain a Dafny "this" expression. /// public ThisExpr(TopLevelDeclWithMembers cl) - : base(cl.tok) { + : base(cl.Origin) { Contract.Requires(cl != null); - Contract.Requires(cl.tok != null); - Type = ModuleResolver.GetThisType(cl.tok, cl); + Contract.Requires(cl.Origin != null); + Type = ModuleResolver.GetThisType(cl.Origin, cl); } public ThisExpr Clone(Cloner cloner) { @@ -46,8 +46,8 @@ public class ImplicitThisExpr : ThisExpr, ICloneable { public ImplicitThisExpr(Cloner cloner, ImplicitThisExpr original) : base(cloner, original) { } - public ImplicitThisExpr(IToken tok) - : base(new AutoGeneratedToken(tok)) { + public ImplicitThisExpr(IOrigin tok) + : base(new AutoGeneratedOrigin(tok)) { Contract.Requires(tok != null); } @@ -66,16 +66,16 @@ public override bool IsImplicit { /// gives a way to distinguish this receiver from other receivers, which /// plays a role in checking the restrictions on divided block statements. /// -public class ImplicitThisExpr_ConstructorCall : ImplicitThisExpr, ICloneable { - public ImplicitThisExpr_ConstructorCall(Cloner cloner, ImplicitThisExpr_ConstructorCall original) : base(cloner, original) { +public class ImplicitThisExprConstructorCall : ImplicitThisExpr, ICloneable { + public ImplicitThisExprConstructorCall(Cloner cloner, ImplicitThisExprConstructorCall original) : base(cloner, original) { } - public ImplicitThisExpr_ConstructorCall(IToken tok) + public ImplicitThisExprConstructorCall(IOrigin tok) : base(tok) { Contract.Requires(tok != null); } - public new ImplicitThisExpr_ConstructorCall Clone(Cloner cloner) { - return new ImplicitThisExpr_ConstructorCall(cloner, this); + public new ImplicitThisExprConstructorCall Clone(Cloner cloner) { + return new ImplicitThisExprConstructorCall(cloner, this); } } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/AttributedExpression.cs b/Source/DafnyCore/AST/Expressions/AttributedExpression.cs index 8ac4b125fa0..91a3ac621c2 100644 --- a/Source/DafnyCore/AST/Expressions/AttributedExpression.cs +++ b/Source/DafnyCore/AST/Expressions/AttributedExpression.cs @@ -4,7 +4,7 @@ namespace Microsoft.Dafny; -public class AttributedExpression : TokenNode, IAttributeBearingDeclaration { +public class AttributedExpression : NodeWithComputedRange, IAttributeBearingDeclaration { public readonly Expression E; public readonly AssertLabel/*?*/ Label; @@ -23,7 +23,9 @@ public Attributes Attributes { } } - public override RangeToken RangeToken => E.RangeToken; + string IAttributeBearingDeclaration.WhatKind => "expression"; + + public override IOrigin Origin => E.Origin; public bool HasAttributes() { return Attributes != null; @@ -37,23 +39,22 @@ public AttributedExpression(Expression e) public AttributedExpression(Expression e, Attributes attrs) : this(e, null, attrs) { } - public AttributedExpression(Expression e, AssertLabel/*?*/ label, Attributes attrs) { + public AttributedExpression(Expression e, AssertLabel/*?*/ label, Attributes attrs) : base(e.Origin) { Contract.Requires(e != null); E = e; Label = label; Attributes = attrs; - this.tok = e.Tok; } - public void AddCustomizedErrorMessage(IToken tok, string s) { + public void AddCustomizedErrorMessage(IOrigin tok, string s) { var args = new List() { new StringLiteralExpr(tok, s, true) }; - IToken openBrace = tok; - IToken closeBrace = new Token(tok.line, tok.col + 7 + s.Length + 1); // where 7 = length(":error ") + IOrigin openBrace = tok; + IOrigin closeBrace = new Token(tok.line, tok.col + 7 + s.Length + 1); // where 7 = length(":error ") this.Attributes = new UserSuppliedAttributes(tok, openBrace, closeBrace, args, this.Attributes); } public override IEnumerable Children => - (Attributes != null ? new List() { Attributes } : Enumerable.Empty()).Concat( + Attributes.AsEnumerable().Concat( new List() { E }); public override IEnumerable PreResolveChildren => Children; diff --git a/Source/DafnyCore/AST/Expressions/AutoGeneratedExpression.cs b/Source/DafnyCore/AST/Expressions/AutoGeneratedExpression.cs index e22ecb327e8..bb42c95a831 100644 --- a/Source/DafnyCore/AST/Expressions/AutoGeneratedExpression.cs +++ b/Source/DafnyCore/AST/Expressions/AutoGeneratedExpression.cs @@ -11,9 +11,9 @@ namespace Microsoft.Dafny; /// // TODO replace this with AutoGeneratedToken. public class AutoGeneratedExpression : ParensExpression, ICloneable { - public AutoGeneratedExpression(IToken tok, Expression e) - : base(tok, e) { - Contract.Requires(tok != null); + public AutoGeneratedExpression(IOrigin origin, Expression e) + : base(origin, e) { + Contract.Requires(origin != null); Contract.Requires(e != null); } @@ -28,9 +28,9 @@ public AutoGeneratedExpression(Cloner cloner, AutoGeneratedExpression original) /// This maker method takes a resolved expression "e" and wraps a resolved AutoGeneratedExpression /// around it. /// - public static AutoGeneratedExpression Create(Expression e, IToken token = null) { + public static AutoGeneratedExpression Create(Expression e, IOrigin token = null) { Contract.Requires(e != null); - var a = new AutoGeneratedExpression(token ?? e.tok, e); + var a = new AutoGeneratedExpression(token ?? e.Origin, e); a.type = e.Type; a.ResolvedExpression = e; return a; diff --git a/Source/DafnyCore/AST/Expressions/Collections/DisplayExpression.cs b/Source/DafnyCore/AST/Expressions/Collections/DisplayExpression.cs index 677dfe896cf..676bd3a690b 100644 --- a/Source/DafnyCore/AST/Expressions/Collections/DisplayExpression.cs +++ b/Source/DafnyCore/AST/Expressions/Collections/DisplayExpression.cs @@ -14,8 +14,8 @@ protected DisplayExpression(Cloner cloner, DisplayExpression original) : base(cl Elements = original.Elements.ConvertAll(cloner.CloneExpr); } - public DisplayExpression(IToken tok, List elements) - : base(tok) { + public DisplayExpression(IOrigin origin, List elements) + : base(origin) { Contract.Requires(cce.NonNullElements(elements)); Elements = elements; } diff --git a/Source/DafnyCore/AST/Expressions/Collections/MapDisplayExpr.cs b/Source/DafnyCore/AST/Expressions/Collections/MapDisplayExpr.cs index b4994544422..f1c90c2769e 100644 --- a/Source/DafnyCore/AST/Expressions/Collections/MapDisplayExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Collections/MapDisplayExpr.cs @@ -13,9 +13,9 @@ public MapDisplayExpr(Cloner cloner, MapDisplayExpr original) : base(cloner, ori Elements = original.Elements.Select(p => new ExpressionPair(cloner.CloneExpr(p.A), cloner.CloneExpr(p.B))).ToList(); } - public MapDisplayExpr(IToken tok, bool finite, List elements) - : base(tok) { - Contract.Requires(tok != null); + public MapDisplayExpr(IOrigin origin, bool finite, List elements) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(cce.NonNullElements(elements)); Finite = finite; Elements = elements; diff --git a/Source/DafnyCore/AST/Expressions/Collections/MultiSetDisplayExpr.cs b/Source/DafnyCore/AST/Expressions/Collections/MultiSetDisplayExpr.cs index 1447da33c6c..17a429e0f15 100644 --- a/Source/DafnyCore/AST/Expressions/Collections/MultiSetDisplayExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Collections/MultiSetDisplayExpr.cs @@ -7,8 +7,8 @@ public class MultiSetDisplayExpr : DisplayExpression, ICloneable elements) : base(tok, elements) { - Contract.Requires(tok != null); + public MultiSetDisplayExpr(IOrigin origin, List elements) : base(origin, elements) { + Contract.Requires(origin != null); Contract.Requires(cce.NonNullElements(elements)); } diff --git a/Source/DafnyCore/AST/Expressions/Collections/MultiSetFormingExpr.cs b/Source/DafnyCore/AST/Expressions/Collections/MultiSetFormingExpr.cs index f7fdab16273..ffb2f187540 100644 --- a/Source/DafnyCore/AST/Expressions/Collections/MultiSetFormingExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Collections/MultiSetFormingExpr.cs @@ -16,9 +16,9 @@ public MultiSetFormingExpr(Cloner cloner, MultiSetFormingExpr original) : base(c } [Captured] - public MultiSetFormingExpr(IToken tok, Expression expr) - : base(tok) { - Contract.Requires(tok != null); + public MultiSetFormingExpr(IOrigin origin, Expression expr) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(expr != null); cce.Owner.AssignSame(this, expr); E = expr; diff --git a/Source/DafnyCore/AST/Expressions/Collections/SeqConstructionExpr.cs b/Source/DafnyCore/AST/Expressions/Collections/SeqConstructionExpr.cs index 7d1fba37d98..456fcb5d2b1 100644 --- a/Source/DafnyCore/AST/Expressions/Collections/SeqConstructionExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Collections/SeqConstructionExpr.cs @@ -15,9 +15,9 @@ public SeqConstructionExpr(Cloner cloner, SeqConstructionExpr original) : base(c ExplicitElementType = elemType; } - public SeqConstructionExpr(IToken tok, Type/*?*/ elementType, Expression length, Expression initializer) - : base(tok) { - Contract.Requires(tok != null); + public SeqConstructionExpr(IOrigin origin, Type/*?*/ elementType, Expression length, Expression initializer) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(length != null); Contract.Requires(initializer != null); ExplicitElementType = elementType; diff --git a/Source/DafnyCore/AST/Expressions/Collections/SeqDisplayExpr.cs b/Source/DafnyCore/AST/Expressions/Collections/SeqDisplayExpr.cs index f739f72f5e1..16c0ee65956 100644 --- a/Source/DafnyCore/AST/Expressions/Collections/SeqDisplayExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Collections/SeqDisplayExpr.cs @@ -4,10 +4,10 @@ namespace Microsoft.Dafny; public class SeqDisplayExpr : DisplayExpression, ICanFormat, ICloneable { - public SeqDisplayExpr(IToken tok, List elements) - : base(tok, elements) { + public SeqDisplayExpr(IOrigin origin, List elements) + : base(origin, elements) { Contract.Requires(cce.NonNullElements(elements)); - Contract.Requires(tok != null); + Contract.Requires(origin != null); } public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { diff --git a/Source/DafnyCore/AST/Expressions/Collections/SeqUpdateExpr.cs b/Source/DafnyCore/AST/Expressions/Collections/SeqUpdateExpr.cs index 1c6b34d2b3f..c3c460e9e08 100644 --- a/Source/DafnyCore/AST/Expressions/Collections/SeqUpdateExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Collections/SeqUpdateExpr.cs @@ -30,9 +30,9 @@ public SeqUpdateExpr(Cloner cloner, SeqUpdateExpr original) : base(cloner, origi Value = cloner.CloneExpr(original.Value); } - public SeqUpdateExpr(IToken tok, Expression seq, Expression index, Expression val) - : base(tok) { - Contract.Requires(tok != null); + public SeqUpdateExpr(IOrigin origin, Expression seq, Expression index, Expression val) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(seq != null); Contract.Requires(index != null); Contract.Requires(val != null); diff --git a/Source/DafnyCore/AST/Expressions/Collections/SetDisplayExpr.cs b/Source/DafnyCore/AST/Expressions/Collections/SetDisplayExpr.cs index b413a85348f..6df90ec9203 100644 --- a/Source/DafnyCore/AST/Expressions/Collections/SetDisplayExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Collections/SetDisplayExpr.cs @@ -5,9 +5,9 @@ namespace Microsoft.Dafny; public class SetDisplayExpr : DisplayExpression, ICanFormat, ICloneable { public bool Finite; - public SetDisplayExpr(IToken tok, bool finite, List elements) - : base(tok, elements) { - Contract.Requires(tok != null); + public SetDisplayExpr(IOrigin origin, bool finite, List elements) + : base(origin, elements) { + Contract.Requires(origin != null); Contract.Requires(cce.NonNullElements(elements)); Finite = finite; } diff --git a/Source/DafnyCore/AST/Expressions/Comprehensions/ComprehensionExpr.cs b/Source/DafnyCore/AST/Expressions/Comprehensions/ComprehensionExpr.cs index cbca3fded02..4f4cb90d6e8 100644 --- a/Source/DafnyCore/AST/Expressions/Comprehensions/ComprehensionExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Comprehensions/ComprehensionExpr.cs @@ -23,7 +23,7 @@ namespace Microsoft.Dafny; /// LambdaExpr also inherits from this base class but isn't really a comprehension, /// and should be considered implementation inheritance. /// -public abstract class ComprehensionExpr : Expression, IAttributeBearingDeclaration, IBoundVarsBearingExpression, ICanFormat { +public abstract partial class ComprehensionExpr : Expression, IAttributeBearingDeclaration, IBoundVarsBearingExpression, ICanFormat { public virtual string WhatKind => "comprehension"; public readonly List BoundVars; public readonly Expression Range; @@ -31,7 +31,7 @@ public abstract class ComprehensionExpr : Expression, IAttributeBearingDeclarati public IEnumerable AllBoundVars => BoundVars; - public IToken BodyStartTok = Token.NoToken; + public IOrigin BodyStartOrigin = Token.NoToken; [ContractInvariantMethod] void ObjectInvariant() { @@ -40,370 +40,9 @@ void ObjectInvariant() { } public Attributes Attributes; - Attributes IAttributeBearingDeclaration.Attributes => Attributes; - - public abstract class BoundedPool : ICloneable { - [Flags] - public enum PoolVirtues { None = 0, Finite = 1, Enumerable = 2, IndependentOfAlloc = 4, IndependentOfAlloc_or_ExplicitAlloc = 8 } - public abstract PoolVirtues Virtues { get; } - /// - /// A higher preference is better. - /// A preference below 2 is a last-resort bounded pool. Bounds discovery will not consider - /// such a pool to be final until there are no other choices. - /// - /// For easy reference, here is the BoundedPool hierarchy and their preference levels: - /// - /// 0: AllocFreeBoundedPool - /// 0: ExplicitAllocatedBoundedPool - /// 0: SpecialAllocIndependenceAllocatedBoundedPool - /// 0: OlderBoundedPool - /// - /// 1: WiggleWaggleBound - /// - /// 2: SuperSetBoundedPool - /// 2: DatatypeInclusionBoundedPool - /// - /// 3: SubSetBoundedPool - /// - /// 4: IntBoundedPool with one bound - /// 5: IntBoundedPool with both bounds - /// 5: CharBoundedPool - /// - /// 8: DatatypeBoundedPool - /// - /// 10: CollectionBoundedPool - /// - SetBoundedPool - /// - MultiSetBoundedPool - /// - MapBoundedPool - /// - SeqBoundedPool - /// - /// 14: BoolBoundedPool - /// - /// 15: ExactBoundedPool - /// - public abstract int Preference(); // higher is better - - public static BoundedPool GetBest(List bounds) { - Contract.Requires(bounds != null); - bounds = CombineIntegerBounds(bounds); - BoundedPool best = null; - foreach (var bound in bounds) { - if (best is IntBoundedPool ibp0 && bound is IntBoundedPool ibp1) { - best = new IntBoundedPool( - ChooseBestIntegerBound(ibp0.LowerBound, ibp1.LowerBound, true), - ChooseBestIntegerBound(ibp0.UpperBound, ibp1.UpperBound, false)); - } else if (best == null || bound.Preference() > best.Preference()) { - best = bound; - } - } - return best; - } - - [CanBeNull] - static Expression ChooseBestIntegerBound([CanBeNull] Expression a, [CanBeNull] Expression b, bool pickMax) { - if (a == null || b == null) { - return a ?? b; - } - - if (Expression.IsIntLiteral(Expression.StripParensAndCasts(a), out var aa) && - Expression.IsIntLiteral(Expression.StripParensAndCasts(b), out var bb)) { - var x = pickMax ? BigInteger.Max(aa, bb) : BigInteger.Min(aa, bb); - return new LiteralExpr(a.tok, x) { Type = a.Type }; - } - return a; // we don't know how to determine which of "a" or "b" is better, so we'll just return "a" - } - - public static List MissingBounds(List vars, List bounds, PoolVirtues requiredVirtues = PoolVirtues.None) where VT : IVariable { - Contract.Requires(vars != null); - Contract.Requires(bounds == null || vars.Count == bounds.Count); - Contract.Ensures(Contract.Result>() != null); - var missing = new List(); - for (var i = 0; i < vars.Count; i++) { - if (bounds == null || bounds[i] == null || (bounds[i].Virtues & requiredVirtues) != requiredVirtues) { - missing.Add(vars[i]); - } - } - return missing; - } - public static List HasBounds(List bounds, PoolVirtues requiredVirtues = PoolVirtues.None) { - Contract.Requires(bounds != null); - Contract.Ensures(Contract.Result>() != null); - Contract.Ensures(Contract.Result>().Count == bounds.Count); - return bounds.ConvertAll(bound => bound != null && (bound.Virtues & requiredVirtues) == requiredVirtues); - } - static List CombineIntegerBounds(List bounds) { - var lowerBounds = new List(); - var upperBounds = new List(); - var others = new List(); - foreach (var b in bounds) { - var ib = b as IntBoundedPool; - if (ib != null && ib.UpperBound == null) { - lowerBounds.Add(ib); - } else if (ib != null && ib.LowerBound == null) { - upperBounds.Add(ib); - } else { - others.Add(b); - } - } - // pair up the bounds - var n = Math.Min(lowerBounds.Count, upperBounds.Count); - for (var i = 0; i < n; i++) { - others.Add(new IntBoundedPool(lowerBounds[i].LowerBound, upperBounds[i].UpperBound)); - } - for (var i = n; i < lowerBounds.Count; i++) { - others.Add(lowerBounds[i]); - } - for (var i = n; i < upperBounds.Count; i++) { - others.Add(upperBounds[i]); - } - return others; - } - - public abstract BoundedPool Clone(Cloner cloner); - } - public class ExactBoundedPool : BoundedPool { - public readonly Expression E; - public ExactBoundedPool(Expression e) { - Contract.Requires(e != null); - E = e; - } - public override PoolVirtues Virtues => PoolVirtues.Finite | PoolVirtues.Enumerable | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - public override int Preference() => 15; // the best of all bounds - public override BoundedPool Clone(Cloner cloner) { - return new ExactBoundedPool(cloner.CloneExpr(E)); - } - } - public class BoolBoundedPool : BoundedPool { - public override PoolVirtues Virtues => PoolVirtues.Finite | PoolVirtues.Enumerable | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - public override int Preference() => 14; - public override BoundedPool Clone(Cloner cloner) { - return this; - } - } - public class CharBoundedPool : BoundedPool { - public override PoolVirtues Virtues => PoolVirtues.Finite | PoolVirtues.Enumerable | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - public override int Preference() => 5; - public override BoundedPool Clone(Cloner cloner) { - return this; - } - } - public class AllocFreeBoundedPool : BoundedPool { - public Type Type; - public AllocFreeBoundedPool(Type t) { - Type = t; - } - public override PoolVirtues Virtues { - get { - if (Type.IsRefType) { - return PoolVirtues.Finite | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - } else { - return PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - } - } - } - public override int Preference() => 0; - public override BoundedPool Clone(Cloner cloner) { - return this; - } - } - public class ExplicitAllocatedBoundedPool : BoundedPool { - public ExplicitAllocatedBoundedPool() { - } - public override PoolVirtues Virtues => PoolVirtues.Finite | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - public override int Preference() => 0; - public override BoundedPool Clone(Cloner cloner) { - return this; - } - } - public class SpecialAllocIndependenceAllocatedBoundedPool : BoundedPool { - public SpecialAllocIndependenceAllocatedBoundedPool() { - } - public override PoolVirtues Virtues => PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - public override int Preference() => 0; - public override BoundedPool Clone(Cloner cloner) { - return this; - } - } - public class IntBoundedPool : BoundedPool { - public readonly Expression LowerBound; - public readonly Expression UpperBound; - public IntBoundedPool(Expression lowerBound, Expression upperBound) { - Contract.Requires(lowerBound != null || upperBound != null); - LowerBound = lowerBound; - UpperBound = upperBound; - } - public override PoolVirtues Virtues { - get { - if (LowerBound != null && UpperBound != null) { - return PoolVirtues.Finite | PoolVirtues.Enumerable | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - } else { - return PoolVirtues.Enumerable | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - } - } - } - public override int Preference() => LowerBound != null && UpperBound != null ? 5 : 4; - public override BoundedPool Clone(Cloner cloner) { - return new IntBoundedPool(cloner.CloneExpr(LowerBound), cloner.CloneExpr(UpperBound)); - } - } - public abstract class CollectionBoundedPool : BoundedPool { - public readonly Type BoundVariableType; - public readonly Type CollectionElementType; - public readonly bool IsFiniteCollection; - - public CollectionBoundedPool(Type bvType, Type collectionElementType, bool isFiniteCollection) { - Contract.Requires(bvType != null); - Contract.Requires(collectionElementType != null); - - BoundVariableType = bvType; - CollectionElementType = collectionElementType; - IsFiniteCollection = isFiniteCollection; - } - - public override PoolVirtues Virtues { - get { - var v = PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - if (IsFiniteCollection) { - v |= PoolVirtues.Finite; - if (CollectionElementType.IsTestableToBe(BoundVariableType)) { - v |= PoolVirtues.Enumerable; - } - } - return v; - } - } - public override int Preference() => 10; - } - public class SetBoundedPool : CollectionBoundedPool { - public readonly Expression Set; - - public SetBoundedPool(Expression set, Type bvType, Type collectionElementType, bool isFiniteCollection) - : base(bvType, collectionElementType, isFiniteCollection) { - Contract.Requires(set != null); - Contract.Requires(bvType != null); - Contract.Requires(collectionElementType != null); - Set = set; - } - - public override BoundedPool Clone(Cloner cloner) { - return new SetBoundedPool(cloner.CloneExpr(Set), BoundVariableType, CollectionElementType, IsFiniteCollection); - } - } - public class SubSetBoundedPool : BoundedPool { - public readonly Expression UpperBound; - public readonly bool IsFiniteCollection; - public SubSetBoundedPool(Expression set, bool isFiniteCollection) { - UpperBound = set; - IsFiniteCollection = isFiniteCollection; - } - public override PoolVirtues Virtues { - get { - if (IsFiniteCollection) { - return PoolVirtues.Finite | PoolVirtues.Enumerable | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - } else { - // it's still enumerable, because at run time, all sets are finite after all - return PoolVirtues.Enumerable | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - } - } - } - public override int Preference() => 3; - public override BoundedPool Clone(Cloner cloner) { - return new SubSetBoundedPool(cloner.CloneExpr(UpperBound), IsFiniteCollection); - } - } - public class SuperSetBoundedPool : BoundedPool { - public readonly Expression LowerBound; - public SuperSetBoundedPool(Expression set) { LowerBound = set; } - public override int Preference() => 2; - public override BoundedPool Clone(Cloner cloner) { - return new SuperSetBoundedPool(cloner.CloneExpr(LowerBound)); - } - - public override PoolVirtues Virtues { - get { - if (LowerBound.Type.MayInvolveReferences) { - return PoolVirtues.None; - } else { - return PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - } - } - } - } - public class MultiSetBoundedPool : CollectionBoundedPool { - public readonly Expression MultiSet; - - public MultiSetBoundedPool(Expression multiset, Type bvType, Type collectionElementType) - : base(bvType, collectionElementType, true) { - Contract.Requires(multiset != null); - Contract.Requires(bvType != null); - Contract.Requires(collectionElementType != null); - MultiSet = multiset; - } - - public override BoundedPool Clone(Cloner cloner) { - return new MultiSetBoundedPool(cloner.CloneExpr(MultiSet), BoundVariableType, CollectionElementType); - } - } - public class MapBoundedPool : CollectionBoundedPool { - public readonly Expression Map; - - public MapBoundedPool(Expression map, Type bvType, Type collectionElementType, bool isFiniteCollection) - : base(bvType, collectionElementType, isFiniteCollection) { - Contract.Requires(map != null); - Contract.Requires(bvType != null); - Contract.Requires(collectionElementType != null); - Map = map; - } - public override BoundedPool Clone(Cloner cloner) { - return new MapBoundedPool(cloner.CloneExpr(Map), BoundVariableType, CollectionElementType, IsFiniteCollection); - } - } - public class SeqBoundedPool : CollectionBoundedPool { - public readonly Expression Seq; - - public SeqBoundedPool(Expression seq, Type bvType, Type collectionElementType) - : base(bvType, collectionElementType, true) { - Contract.Requires(seq != null); - Contract.Requires(bvType != null); - Contract.Requires(collectionElementType != null); - Seq = seq; - } - - public override BoundedPool Clone(Cloner cloner) { - return new SeqBoundedPool(cloner.CloneExpr(Seq), BoundVariableType, CollectionElementType); - } - } - public class DatatypeBoundedPool : BoundedPool { - public readonly DatatypeDecl Decl; - - public DatatypeBoundedPool(DatatypeDecl d) { - Contract.Requires(d != null); - Decl = d; - } - public override PoolVirtues Virtues => PoolVirtues.Finite | PoolVirtues.Enumerable | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - public override int Preference() => 8; - public override BoundedPool Clone(Cloner cloner) { - return this; - } - } - public class DatatypeInclusionBoundedPool : BoundedPool { - public readonly bool IsIndDatatype; - public DatatypeInclusionBoundedPool(bool isIndDatatype) : base() { IsIndDatatype = isIndDatatype; } - public override PoolVirtues Virtues => (IsIndDatatype ? PoolVirtues.Finite : PoolVirtues.None) | PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - public override int Preference() => 2; - public override BoundedPool Clone(Cloner cloner) { - return this; - } - } - - public class OlderBoundedPool : BoundedPool { - public OlderBoundedPool() { - } - public override PoolVirtues Virtues => PoolVirtues.IndependentOfAlloc | PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc; - public override int Preference() => 0; - public override BoundedPool Clone(Cloner cloner) { - return this; - } + Attributes IAttributeBearingDeclaration.Attributes { + get => Attributes; + set => Attributes = value; } [FilledInDuringResolution] public List Bounds; @@ -412,12 +51,12 @@ public override BoundedPool Clone(Cloner cloner) { public List UncompilableBoundVars() { Contract.Ensures(Contract.Result>() != null); var v = BoundedPool.PoolVirtues.Finite | BoundedPool.PoolVirtues.Enumerable; - return ComprehensionExpr.BoundedPool.MissingBounds(BoundVars, Bounds, v); + return BoundedPool.MissingBounds(BoundVars, Bounds, v); } - public ComprehensionExpr(IToken tok, RangeToken rangeToken, List bvars, Expression range, Expression term, Attributes attrs) - : base(tok) { - Contract.Requires(tok != null); + protected ComprehensionExpr(IOrigin origin, List bvars, Expression range, Expression term, Attributes attrs) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(cce.NonNullElements(bvars)); Contract.Requires(term != null); @@ -425,16 +64,14 @@ public ComprehensionExpr(IToken tok, RangeToken rangeToken, List bvars Range = range; Term = term; Attributes = attrs; - BodyStartTok = tok; - RangeToken = rangeToken; + BodyStartOrigin = origin; } protected ComprehensionExpr(Cloner cloner, ComprehensionExpr original) : base(cloner, original) { BoundVars = original.BoundVars.Select(bv => cloner.CloneBoundVar(bv, false)).ToList(); Range = cloner.CloneExpr(original.Range); Attributes = cloner.CloneAttributes(original.Attributes); - BodyStartTok = cloner.Tok(original.BodyStartTok); - RangeToken = cloner.Tok(original.RangeToken); + BodyStartOrigin = cloner.Origin(original.BodyStartOrigin); Term = cloner.CloneExpr(original.Term); if (cloner.CloneResolvedFields) { @@ -442,13 +79,13 @@ protected ComprehensionExpr(Cloner cloner, ComprehensionExpr original) : base(cl } } public override IEnumerable Children => - (Attributes != null ? new List { Attributes } : Enumerable.Empty()). - Concat(BoundVars).Concat(SubExpressions); + Attributes.AsEnumerable(). + Concat(BoundVars).Concat(SubExpressions); public override IEnumerable PreResolveChildren => - (Attributes != null ? new List { Attributes } : Enumerable.Empty()) - .Concat(Range != null && Range.tok.line > 0 ? new List() { Range } : new List()) - .Concat(Term != null && Term.tok.line > 0 ? new List { Term } : new List()); + Attributes.AsEnumerable() + .Concat(Range != null && Range.Origin.line > 0 ? new List() { Range } : new List()) + .Concat(Term != null && Term.Origin.line > 0 ? new List { Term } : new List()); public override IEnumerable SubExpressions { get { diff --git a/Source/DafnyCore/AST/Expressions/Comprehensions/ExistsExpr.cs b/Source/DafnyCore/AST/Expressions/Comprehensions/ExistsExpr.cs index c021f943bac..52eac7cfdc3 100644 --- a/Source/DafnyCore/AST/Expressions/Comprehensions/ExistsExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Comprehensions/ExistsExpr.cs @@ -5,12 +5,12 @@ namespace Microsoft.Dafny; public class ExistsExpr : QuantifierExpr, ICloneable { public override string WhatKind => "exists expression"; - protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.Or; } } + protected override BinaryExpr.ResolvedOpcode SplitResolvedOp => BinaryExpr.ResolvedOpcode.Or; - public ExistsExpr(IToken tok, RangeToken rangeToken, List bvars, Expression range, Expression term, Attributes attrs) - : base(tok, rangeToken, bvars, range, term, attrs) { + public ExistsExpr(IOrigin origin, List bvars, Expression range, Expression term, Attributes attrs) + : base(origin, bvars, range, term, attrs) { Contract.Requires(cce.NonNullElements(bvars)); - Contract.Requires(tok != null); + Contract.Requires(origin != null); Contract.Requires(term != null); } @@ -25,9 +25,44 @@ public override Expression LogicalBody(bool bypassSplitQuantifier = false) { if (Range == null) { return Term; } - var body = new BinaryExpr(Term.tok, BinaryExpr.Opcode.And, Range, Term); + var body = new BinaryExpr(Term.Origin, BinaryExpr.Opcode.And, Range, Term); body.ResolvedOp = BinaryExpr.ResolvedOpcode.And; body.Type = Term.Type; return body; } + + /// + /// Returns an expression like 'exists' but where the bound variables have been renamed to have + /// 'prefix' as a prefix to their previous names. + /// Assumes the expression has been resolved. + /// + public Expression AlphaRename(string prefix) { + Contract.Requires(this != null); + Contract.Requires(prefix != null); + + if (SplitQuantifier != null) { + // TODO: what to do? Substitute(exists.SplitQuantifierExpression); + } + + var substMap = new Dictionary(); + var var4var = new Dictionary(); + var bvars = new List(); + foreach (var bv in BoundVars) { + var newBv = new BoundVar(bv.Origin, prefix + bv.Name, bv.Type); + bvars.Add(newBv); + var4var.Add(bv, newBv); + var ie = new IdentifierExpr(newBv.Origin, newBv.Name); + ie.Var = newBv; // resolve here + ie.Type = newBv.Type; // resolve here + substMap.Add(bv, ie); + } + var s = new Substituter(null, substMap, new Dictionary()); + var range = Range == null ? null : s.Substitute(Range); + var term = s.Substitute(Term); + var attrs = s.SubstAttributes(Attributes); + var ex = new ExistsExpr(Origin, bvars, range, term, attrs); + ex.Type = Type.Bool; + ex.Bounds = s.SubstituteBoundedPoolList(Bounds); + return ex; + } } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/Comprehensions/ForallExpr.cs b/Source/DafnyCore/AST/Expressions/Comprehensions/ForallExpr.cs index 36f29c10f70..9b03a7712bc 100644 --- a/Source/DafnyCore/AST/Expressions/Comprehensions/ForallExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Comprehensions/ForallExpr.cs @@ -5,12 +5,12 @@ namespace Microsoft.Dafny; public class ForallExpr : QuantifierExpr, ICloneable { public override string WhatKind => "forall expression"; - protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.And; } } + protected override BinaryExpr.ResolvedOpcode SplitResolvedOp => BinaryExpr.ResolvedOpcode.And; - public ForallExpr(IToken tok, RangeToken rangeToken, List bvars, Expression range, Expression term, Attributes attrs) - : base(tok, rangeToken, bvars, range, term, attrs) { + public ForallExpr(IOrigin origin, List bvars, Expression range, Expression term, Attributes attrs) + : base(origin, bvars, range, term, attrs) { Contract.Requires(cce.NonNullElements(bvars)); - Contract.Requires(tok != null); + Contract.Requires(origin != null); Contract.Requires(term != null); } @@ -25,7 +25,7 @@ public override Expression LogicalBody(bool bypassSplitQuantifier = false) { if (Range == null) { return Term; } - var body = new BinaryExpr(Term.tok, BinaryExpr.Opcode.Imp, Range, Term); + var body = new BinaryExpr(Term.Origin, BinaryExpr.Opcode.Imp, Range, Term); body.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp; body.Type = Term.Type; return body; diff --git a/Source/DafnyCore/AST/Expressions/Comprehensions/LambdaExpr.cs b/Source/DafnyCore/AST/Expressions/Comprehensions/LambdaExpr.cs index 14f73e4b845..e745237bf34 100644 --- a/Source/DafnyCore/AST/Expressions/Comprehensions/LambdaExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Comprehensions/LambdaExpr.cs @@ -3,15 +3,15 @@ namespace Microsoft.Dafny; -public class LambdaExpr : ComprehensionExpr, ICloneable { - public override string WhatKind => "lambda"; +public class LambdaExpr : ComprehensionExpr, ICloneable, IFrameScope { + public override string WhatKind => Reads.Expressions.Count != 0 ? "lambda" : Range != null ? "partial lambda" : "total lambda"; public Expression Body => Term; - public readonly List Reads; + public readonly Specification Reads; - public LambdaExpr(IToken tok, RangeToken rangeToken, List bvars, Expression requires, List reads, Expression body) - : base(tok, rangeToken, bvars, requires, body, null) { + public LambdaExpr(IOrigin origin, List bvars, Expression requires, Specification reads, Expression body) + : base(origin, bvars, requires, body, null) { Contract.Requires(reads != null); Reads = reads; } @@ -22,14 +22,14 @@ public override IEnumerable SubExpressions { if (Range != null) { yield return Range; } - foreach (var read in Reads) { + foreach (var read in Reads.Expressions) { yield return read.E; } } } public LambdaExpr(Cloner cloner, LambdaExpr original) : base(cloner, original) { - Reads = original.Reads.ConvertAll(cloner.CloneFrameExpr); + Reads = cloner.CloneSpecFrameExpr(original.Reads); } public LambdaExpr Clone(Cloner cloner) { @@ -84,4 +84,6 @@ public override bool SetIndent(int indentBefore, TokenNewIndentCollector formatt return true; } + + public string Designator => "lambda"; } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/Comprehensions/MapComprehension.cs b/Source/DafnyCore/AST/Expressions/Comprehensions/MapComprehension.cs index 38022ff60fb..1cdfe7c3acd 100644 --- a/Source/DafnyCore/AST/Expressions/Comprehensions/MapComprehension.cs +++ b/Source/DafnyCore/AST/Expressions/Comprehensions/MapComprehension.cs @@ -20,9 +20,9 @@ public MapComprehension(Cloner cloner, MapComprehension original) : base(cloner, Finite = original.Finite; } - public MapComprehension(IToken tok, RangeToken rangeToken, bool finite, List bvars, Expression range, Expression/*?*/ termLeft, Expression termRight, Attributes attrs) - : base(tok, rangeToken, bvars, range, termRight, attrs) { - Contract.Requires(tok != null); + public MapComprehension(IOrigin origin, bool finite, List bvars, Expression range, Expression/*?*/ termLeft, Expression termRight, Attributes attrs) + : base(origin, bvars, range, termRight, attrs) { + Contract.Requires(origin != null); Contract.Requires(cce.NonNullElements(bvars)); Contract.Requires(1 <= bvars.Count); Contract.Requires(range != null); diff --git a/Source/DafnyCore/AST/Expressions/Comprehensions/QuantifierExpr.cs b/Source/DafnyCore/AST/Expressions/Comprehensions/QuantifierExpr.cs index 5860a67c761..28db8b57af7 100644 --- a/Source/DafnyCore/AST/Expressions/Comprehensions/QuantifierExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Comprehensions/QuantifierExpr.cs @@ -11,13 +11,13 @@ public abstract class QuantifierExpr : ComprehensionExpr, TypeParameter.ParentTy private readonly int UniqueId; private static int currentQuantId = -1; - protected virtual BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.Or; } } + protected virtual BinaryExpr.ResolvedOpcode SplitResolvedOp => BinaryExpr.ResolvedOpcode.Or; private Expression SplitQuantifierToExpression() { Contract.Requires(SplitQuantifier != null && SplitQuantifier.Any()); Expression accumulator = SplitQuantifier[0]; for (int tid = 1; tid < SplitQuantifier.Count; tid++) { - accumulator = new BinaryExpr(Term.tok, SplitResolvedOp, accumulator, SplitQuantifier[tid]); + accumulator = new BinaryExpr(Term.Origin, SplitResolvedOp, accumulator, SplitQuantifier[tid]); } return accumulator; } @@ -50,9 +50,9 @@ public String Refresh(string prefix, FreshIdGenerator idGen) { return idGen.FreshId(prefix); } - public QuantifierExpr(IToken tok, RangeToken rangeToken, List bvars, Expression range, Expression term, Attributes attrs) - : base(tok, rangeToken, bvars, range, term, attrs) { - Contract.Requires(tok != null); + protected QuantifierExpr(IOrigin origin, List bvars, Expression range, Expression term, Attributes attrs) + : base(origin, bvars, range, term, attrs) { + Contract.Requires(origin != null); Contract.Requires(cce.NonNullElements(bvars)); Contract.Requires(term != null); this.UniqueId = FreshQuantId(); @@ -78,14 +78,14 @@ public virtual Expression LogicalBody(bool bypassSplitQuantifier = false) { public override IEnumerable SubExpressions { get { - if (SplitQuantifier == null) { - foreach (var e in base.SubExpressions) { - yield return e; - } - } else { - foreach (var e in Attributes.SubExpressions(Attributes)) { - yield return e; - } + foreach (var e in base.SubExpressions) { + yield return e; + } + foreach (var e in Attributes.SubExpressions(Attributes)) { + yield return e; + } + + if (SplitQuantifier != null) { foreach (var e in SplitQuantifier) { yield return e; } diff --git a/Source/DafnyCore/AST/Expressions/Comprehensions/SetComprehension.cs b/Source/DafnyCore/AST/Expressions/Comprehensions/SetComprehension.cs index 0dc1a998664..831a359655c 100644 --- a/Source/DafnyCore/AST/Expressions/Comprehensions/SetComprehension.cs +++ b/Source/DafnyCore/AST/Expressions/Comprehensions/SetComprehension.cs @@ -27,9 +27,9 @@ public SetComprehension(Cloner cloner, SetComprehension original) : base(cloner, Finite = original.Finite; } - public SetComprehension(IToken tok, RangeToken rangeToken, bool finite, List bvars, Expression range, Expression/*?*/ term, Attributes attrs) - : base(tok, rangeToken, bvars, range, term ?? new IdentifierExpr(tok, bvars[0].Name), attrs) { - Contract.Requires(tok != null); + public SetComprehension(IOrigin origin, bool finite, List bvars, Expression range, Expression/*?*/ term, Attributes attrs) + : base(origin, bvars, range, term ?? new IdentifierExpr(origin, bvars[0].Name), attrs) { + Contract.Requires(origin != null); Contract.Requires(cce.NonNullElements(bvars)); Contract.Requires(1 <= bvars.Count); Contract.Requires(range != null); diff --git a/Source/DafnyCore/AST/Expressions/ConcreteSyntaxExpression.cs b/Source/DafnyCore/AST/Expressions/ConcreteSyntaxExpression.cs index 69bda638311..26ebc0ed5cc 100644 --- a/Source/DafnyCore/AST/Expressions/ConcreteSyntaxExpression.cs +++ b/Source/DafnyCore/AST/Expressions/ConcreteSyntaxExpression.cs @@ -15,21 +15,14 @@ protected ConcreteSyntaxExpression(Cloner cloner, ConcreteSyntaxExpression origi } } + /// + /// // after resolution, manipulation of "this" should proceed as with manipulating "this.ResolvedExpression" + /// [FilledInDuringResolution] - private Expression resolvedExpression; + public Expression ResolvedExpression { get; set; } - public Expression ResolvedExpression { - get => resolvedExpression; - set { - resolvedExpression = value; - if (rangeToken != null && resolvedExpression != null) { - resolvedExpression.RangeToken = rangeToken; - } - } - } // after resolution, manipulation of "this" should proceed as with manipulating "this.ResolvedExpression" - - public ConcreteSyntaxExpression(IToken tok) - : base(tok) { + protected ConcreteSyntaxExpression(IOrigin origin) + : base(origin) { } public override IEnumerable Children => ResolvedExpression == null ? Array.Empty() : new[] { ResolvedExpression }; public override IEnumerable SubExpressions { diff --git a/Source/DafnyCore/AST/Expressions/Conditional/ITEExpr.cs b/Source/DafnyCore/AST/Expressions/Conditional/ITEExpr.cs index aa6e606c27c..78ac3f162e8 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/ITEExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/ITEExpr.cs @@ -30,9 +30,9 @@ void ObjectInvariant() { Contract.Invariant(Els != null); } - public ITEExpr(IToken tok, bool isBindingGuard, Expression test, Expression thn, Expression els) - : base(tok) { - Contract.Requires(tok != null); + public ITEExpr(IOrigin origin, bool isBindingGuard, Expression test, Expression thn, Expression els) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(test != null); Contract.Requires(thn != null); Contract.Requires(els != null); @@ -64,7 +64,7 @@ public override IEnumerable TerminalExpressions { public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { var lineThen = 0; var colThen = 0; - IToken thenToken = null; + IOrigin thenToken = null; foreach (var token in OwnedTokens) { switch (token.val) { case "if": { diff --git a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCase.cs b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCase.cs index 0897f4bbb69..be2227d0021 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCase.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCase.cs @@ -2,13 +2,12 @@ namespace Microsoft.Dafny; -public abstract class NestedMatchCase : TokenNode { +public abstract class NestedMatchCase : NodeWithComputedRange { public readonly ExtendedPattern Pat; - public NestedMatchCase(IToken tok, ExtendedPattern pat) { - Contract.Requires(tok != null); + protected NestedMatchCase(IOrigin origin, ExtendedPattern pat) : base(origin) { + Contract.Requires(origin != null); Contract.Requires(pat != null); - this.tok = tok; this.Pat = pat; } diff --git a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCaseExpr.cs b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCaseExpr.cs index 87f15ac5fcd..e831cb82d9d 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCaseExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCaseExpr.cs @@ -1,23 +1,26 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.IO; using System.Linq; namespace Microsoft.Dafny; public class NestedMatchCaseExpr : NestedMatchCase, IAttributeBearingDeclaration { public Expression Body; - public Attributes Attributes; - Attributes IAttributeBearingDeclaration.Attributes => Attributes; + public Attributes Attributes { get; set; } - public NestedMatchCaseExpr(IToken tok, ExtendedPattern pat, Expression body, Attributes attrs) : base(tok, pat) { + string IAttributeBearingDeclaration.WhatKind => "match expression case"; + + public NestedMatchCaseExpr(IOrigin origin, ExtendedPattern pat, Expression body, Attributes attrs) : base(origin, pat) { Contract.Requires(body != null); this.Body = body; this.Attributes = attrs; } public override IEnumerable Children => - (Attributes != null ? new Node[] { Attributes } : Enumerable.Empty()).Concat(new Node[] { Body, Pat }); + Attributes.AsEnumerable(). + Concat(new Node[] { Body, Pat }); public override IEnumerable PreResolveChildren => Children; @@ -33,7 +36,13 @@ public void Resolve(ModuleResolver resolver, var afterResolveErrorCount = resolver.reporter.ErrorCount; if (beforeResolveErrorCount == afterResolveErrorCount) { resolver.ResolveExpression(Body, resolutionContext); - resolver.ConstrainSubtypeRelation(resultType, Body.Type, Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", Body.Type, resultType); + resolver.ConstrainSubtypeRelation(resultType, Body.Type, Body.Origin, "type of case bodies do not agree (found {0}, previous types {1})", Body.Type, resultType); } } + + public override string ToString() { + var writer = new StringWriter(); + new Printer(writer, DafnyOptions.Default).PrintNestedMatchCase(false, false, this, false, false); + return writer.ToString(); + } } diff --git a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCaseStmt.cs b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCaseStmt.cs index d9f33f87712..c3a5bd436c9 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCaseStmt.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchCaseStmt.cs @@ -7,22 +7,21 @@ namespace Microsoft.Dafny; public class NestedMatchCaseStmt : NestedMatchCase, IAttributeBearingDeclaration, ICloneable { public readonly List Body; - public Attributes Attributes; - Attributes IAttributeBearingDeclaration.Attributes => Attributes; - public NestedMatchCaseStmt(RangeToken rangeToken, ExtendedPattern pat, List body) : base(rangeToken.StartToken, pat) { - RangeToken = rangeToken; + public Attributes Attributes { get; set; } + string IAttributeBearingDeclaration.WhatKind => "match statement case"; + public NestedMatchCaseStmt(IOrigin rangeOrigin, ExtendedPattern pat, List body) : base(rangeOrigin, pat) { Contract.Requires(body != null); this.Body = body; this.Attributes = null; } - public NestedMatchCaseStmt(IToken tok, ExtendedPattern pat, List body, Attributes attrs) : base(tok, pat) { + public NestedMatchCaseStmt(IOrigin origin, ExtendedPattern pat, List body, Attributes attrs) : base(origin, pat) { Contract.Requires(body != null); this.Body = body; this.Attributes = attrs; } - private NestedMatchCaseStmt(Cloner cloner, NestedMatchCaseStmt original) : base(original.tok, original.Pat) { - this.Body = original.Body.Select(cloner.CloneStmt).ToList(); + private NestedMatchCaseStmt(Cloner cloner, NestedMatchCaseStmt original) : base(original.Origin, original.Pat) { + this.Body = original.Body.Select(stmt => cloner.CloneStmt(stmt, false)).ToList(); this.Attributes = cloner.CloneAttributes(original.Attributes); } @@ -37,16 +36,16 @@ public void Resolve( ResolutionContext resolutionContext, Dictionary subst, Type sourceType) { - var beforeResolveErrorCount = resolver.reporter.ErrorCount; + var beforeResolveErrorCount = resolver.Reporter.ErrorCount; Pat.Resolve(resolver, resolutionContext, sourceType, resolutionContext.IsGhost, true, false, false); // In Dafny, any bound variables introduced in a pattern are in scope throughout the case body, and cannot be shadowed at the top-level // of the case body. Because the machinery above creates, for each bound variable, a local variable with the same name and declares that // local variable in the case body, we introduce a new scope boundary around the body. - resolver.scope.PushMarker(); + resolver.Scope.PushMarker(); resolver.ResolveAttributes(this, resolutionContext); - var afterResolveErrorCount = resolver.reporter.ErrorCount; + var afterResolveErrorCount = resolver.Reporter.ErrorCount; if (beforeResolveErrorCount == afterResolveErrorCount) { resolver.DominatingStatementLabels.PushMarker(); foreach (Statement ss in Body) { @@ -54,6 +53,6 @@ public void Resolve( } resolver.DominatingStatementLabels.PopMarker(); } - resolver.scope.PopMarker(); + resolver.Scope.PopMarker(); } } diff --git a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchExpr.cs b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchExpr.cs index f362acdd8dd..98914aa9457 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchExpr.cs @@ -8,12 +8,16 @@ namespace Microsoft.Dafny; interface INestedMatch : INode { Expression Source { get; } string MatchTypeName { get; } + IReadOnlyList Cases { get; } } public class NestedMatchExpr : Expression, ICloneable, ICanFormat, INestedMatch { public Expression Source { get; } public string MatchTypeName => "expression"; - public readonly List Cases; + public List Cases { get; } + + IReadOnlyList INestedMatch.Cases => Cases; + public readonly bool UsesOptionalBraces; public Attributes Attributes; @@ -24,12 +28,13 @@ public NestedMatchExpr(Cloner cloner, NestedMatchExpr original) : base(cloner, o this.Source = cloner.CloneExpr(original.Source); this.Cases = original.Cases.Select(cloner.CloneNestedMatchCaseExpr).ToList(); this.UsesOptionalBraces = original.UsesOptionalBraces; + if (cloner.CloneResolvedFields) { Flattened = cloner.CloneExpr(original.Flattened); } } - public NestedMatchExpr(IToken tok, Expression source, [Captured] List cases, bool usesOptionalBraces, Attributes attrs = null) : base(tok) { + public NestedMatchExpr(IOrigin origin, Expression source, [Captured] List cases, bool usesOptionalBraces, Attributes attrs = null) : base(origin) { Contract.Requires(source != null); Contract.Requires(cce.NonNullElements(cases)); this.Source = source; @@ -60,13 +65,13 @@ public void Resolve(ModuleResolver resolver, ResolutionContext resolutionContext resolver.PartiallySolveTypeConstraints(true); if (Source.Type is TypeProxy) { - resolver.reporter.Error(MessageSource.Resolver, tok, "Could not resolve the type of the source of the match expression. Please provide additional typing annotations."); + resolver.reporter.Error(MessageSource.Resolver, Origin, "Could not resolve the type of the source of the match expression. Please provide additional typing annotations."); return; } } var errorCount = resolver.reporter.Count(ErrorLevel.Error); - var sourceType = resolver.PartiallyResolveTypeForMemberSelection(Source.tok, Source.Type).NormalizeExpand(); + var sourceType = resolver.PartiallyResolveTypeForMemberSelection(Source.Origin, Source.Type).NormalizeExpand(); if (resolver.reporter.Count(ErrorLevel.Error) != errorCount) { return; } diff --git a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchStmt.cs b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchStmt.cs index 9afe86731ca..b585aa1de4c 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchStmt.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/NestedMatchStmt.cs @@ -8,19 +8,22 @@ namespace Microsoft.Dafny; public class NestedMatchStmt : Statement, ICloneable, ICanFormat, INestedMatch, ICanResolve { public Expression Source { get; } public string MatchTypeName => "statement"; - public readonly List Cases; + public List Cases { get; } + + IReadOnlyList INestedMatch.Cases => Cases; + public readonly bool UsesOptionalBraces; [FilledInDuringResolution] public Statement Flattened { get; set; } private void InitializeAttributes() { // Default case for match is false - bool splitMatch = Attributes.Contains(this.Attributes, "split"); - Attributes.ContainsBool(this.Attributes, "split", ref splitMatch); - foreach (var c in this.Cases) { + bool splitMatch = Attributes.Contains(Attributes, "split"); + Attributes.ContainsBool(Attributes, "split", ref splitMatch); + foreach (var c in Cases) { if (!Attributes.Contains(c.Attributes, "split")) { List args = new List(); - args.Add(new LiteralExpr(c.Tok, splitMatch)); + args.Add(Expression.CreateBoolLiteral(c.Origin, splitMatch)); Attributes attrs = new Attributes("split", args, c.Attributes); c.Attributes = attrs; } @@ -36,16 +39,23 @@ public NestedMatchStmt(Cloner cloner, NestedMatchStmt original) : base(cloner, o Cases = original.Cases.ConvertAll(cloner.CloneNestedMatchCaseStmt); UsesOptionalBraces = original.UsesOptionalBraces; if (cloner.CloneResolvedFields) { - Flattened = cloner.CloneStmt(original.Flattened); + Flattened = cloner.CloneStmt(original.Flattened, false); } } public override IEnumerable Children => new[] { Source }.Concat(Cases); - public override IEnumerable SubStatements => Cases.SelectMany(c => c.Body); + public override IEnumerable SubStatements { + get { + if (Flattened != null) { + return Flattened.SubStatements; + } + return Cases.SelectMany(c => c.Body); + } + } public override IEnumerable PreResolveSubStatements { - get => this.Cases.SelectMany(oneCase => oneCase.Body); + get => Cases.SelectMany(oneCase => oneCase.Body); } public override IEnumerable NonSpecificationSubExpressions { @@ -62,13 +72,13 @@ public override IEnumerable NonSpecificationSubExpressions { } } - public NestedMatchStmt(RangeToken rangeToken, Expression source, [Captured] List cases, bool usesOptionalBraces, Attributes attrs = null) - : base(rangeToken, attrs) { + public NestedMatchStmt(IOrigin origin, Expression source, [Captured] List cases, bool usesOptionalBraces, Attributes attrs = null) + : base(origin, attrs) { Contract.Requires(source != null); Contract.Requires(cce.NonNullElements(cases)); - this.Source = source; - this.Cases = cases; - this.UsesOptionalBraces = usesOptionalBraces; + Source = source; + Cases = cases; + UsesOptionalBraces = usesOptionalBraces; InitializeAttributes(); } @@ -78,22 +88,22 @@ public NestedMatchStmt(RangeToken rangeToken, Expression source, [Captured] List /// 2 - desugaring it into a decision tree of MatchStmt and IfStmt (for constant matching) /// 3 - resolving the generated (sub)statement. /// - public override void Resolve(ModuleResolver resolver, ResolutionContext resolutionContext) { + public void Resolve(ModuleResolver resolver, ResolutionContext resolutionContext) { resolver.ResolveExpression(Source, resolutionContext); if (Source.Type is TypeProxy) { resolver.PartiallySolveTypeConstraints(true); if (Source.Type is TypeProxy) { - resolver.reporter.Error(MessageSource.Resolver, Tok, "Could not resolve the type of the source of the match statement. Please provide additional typing annotations."); + resolver.Reporter.Error(MessageSource.Resolver, Origin, "Could not resolve the type of the source of the match statement. Please provide additional typing annotations."); return; } } - var errorCount = resolver.reporter.Count(ErrorLevel.Error); - var sourceType = resolver.PartiallyResolveTypeForMemberSelection(Source.tok, Source.Type).NormalizeExpand(); - this.CheckLinearNestedMatchStmt(sourceType, resolutionContext, resolver); - if (resolver.reporter.Count(ErrorLevel.Error) != errorCount) { + var errorCount = resolver.Reporter.Count(ErrorLevel.Error); + var sourceType = resolver.PartiallyResolveTypeForMemberSelection(Source.Origin, Source.Type).NormalizeExpand(); + CheckLinearNestedMatchStmt(sourceType, resolutionContext, resolver); + if (resolver.Reporter.Count(ErrorLevel.Error) != errorCount) { return; } @@ -105,14 +115,14 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti } foreach (var _case in Cases) { - resolver.scope.PushMarker(); + resolver.Scope.PushMarker(); _case.Resolve(resolver, resolutionContext, subst, sourceType); - resolver.scope.PopMarker(); + resolver.Scope.PopMarker(); } } public void CheckLinearNestedMatchStmt(Type dtd, ResolutionContext resolutionContext, ModuleResolver resolver) { - foreach (NestedMatchCaseStmt mc in this.Cases) { + foreach (NestedMatchCaseStmt mc in Cases) { resolver.scope.PushMarker(); resolver.ResolveAttributes(mc, resolutionContext); mc.CheckLinearNestedMatchCase(dtd, resolutionContext, resolver); @@ -130,4 +140,31 @@ public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { } }); } + + public override void ResolveGhostness(ModuleResolver resolver, ErrorReporter reporter, bool mustBeErasable, + ICodeContext codeContext, string proofContext, + bool allowAssumptionVariables, bool inConstructorInitializationPhase) { + var hasGhostPattern = Cases. + SelectMany(caze => caze.Pat.DescendantsAndSelf) + .OfType().Any(idPattern => idPattern.Ctor != null && idPattern.Ctor.IsGhost); + IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(Source) || hasGhostPattern; + + foreach (var kase in Cases) { + ExpressionTester.MakeGhostAsNeeded(kase.Pat, IsGhost); + } + + if (!mustBeErasable && IsGhost) { + reporter.Info(MessageSource.Resolver, Origin, "ghost match"); + } + + Cases.ForEach(kase => kase.Body.ForEach(ss => + ss.ResolveGhostness(resolver, reporter, IsGhost, codeContext, + proofContext, allowAssumptionVariables, inConstructorInitializationPhase))); + IsGhost = IsGhost || Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); + if (!IsGhost) { + // If there were features in the source expression that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, Source, codeContext); + } + } } diff --git a/Source/DafnyCore/AST/Expressions/Conditional/Patterns/DisjunctivePattern.cs b/Source/DafnyCore/AST/Expressions/Conditional/Patterns/DisjunctivePattern.cs index da15ae183f4..63122640b62 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/Patterns/DisjunctivePattern.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/Patterns/DisjunctivePattern.cs @@ -6,7 +6,7 @@ namespace Microsoft.Dafny; public class DisjunctivePattern : ExtendedPattern { public readonly List Alternatives; - public DisjunctivePattern(IToken tok, List alternatives, bool isGhost = false) : base(tok, isGhost) { + public DisjunctivePattern(IOrigin origin, List alternatives, bool isGhost = false) : base(origin, isGhost) { Contract.Requires(alternatives != null && alternatives.Count > 0); this.Alternatives = alternatives; } @@ -29,7 +29,7 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti bool inPattern, bool inDisjunctivePattern) { if (inPattern) { - resolver.reporter.Error(MessageSource.Resolver, Tok, "Disjunctive patterns are not allowed inside other patterns"); + resolver.reporter.Error(MessageSource.Resolver, Origin, "Disjunctive patterns are not allowed inside other patterns"); } foreach (var alternative in Alternatives) { diff --git a/Source/DafnyCore/AST/Expressions/Conditional/Patterns/ExtendedPattern.cs b/Source/DafnyCore/AST/Expressions/Conditional/Patterns/ExtendedPattern.cs index 43a94fe1b36..28a21660987 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/Patterns/ExtendedPattern.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/Patterns/ExtendedPattern.cs @@ -11,12 +11,11 @@ namespace Microsoft.Dafny; 2 - An IdPattern of a string and a list of ExtendedPattern, representing either a bound variable or a constructor applied to n arguments or a symbolic constant */ -public abstract class ExtendedPattern : TokenNode { +public abstract class ExtendedPattern : NodeWithComputedRange { public bool IsGhost; - public ExtendedPattern(IToken tok, bool isGhost = false) { - Contract.Requires(tok != null); - this.tok = tok; + public ExtendedPattern(IOrigin origin, bool isGhost = false) : base(origin) { + Contract.Requires(origin != null); this.IsGhost = isGhost; } @@ -64,9 +63,9 @@ public void CheckLinearExtendedPattern(Type type, ResolutionContext resolutionCo if (idPattern.Arguments != null) { // pat is a tuple or constructor if (idPattern.Id.StartsWith(SystemModuleManager.TupleTypeCtorNamePrefix)) { - resolver.reporter.Error(MessageSource.Resolver, this.Tok, $"tuple type does not match type {type.ToString()}"); + resolver.reporter.Error(MessageSource.Resolver, this.Origin, $"tuple type does not match type {type.ToString()}"); } else { - resolver.reporter.Error(MessageSource.Resolver, this.Tok, $"member {idPattern.Id} does not exist in type {type.ToString()}"); + resolver.reporter.Error(MessageSource.Resolver, this.Origin, $"member {idPattern.Id} does not exist in type {type.ToString()}"); } } else { // pat is a simple variable or a constant /* =[1]= */ @@ -82,7 +81,7 @@ public void CheckLinearExtendedPattern(Type type, ResolutionContext resolutionCo } else if (type.AsDatatype is TupleTypeDecl tupleTypeDecl) { var udt = type.NormalizeExpand() as UserDefinedType; if (!(this is IdPattern)) { - resolver.reporter.Error(MessageSource.Resolver, this.Tok, "pattern doesn't correspond to a tuple"); + resolver.reporter.Error(MessageSource.Resolver, this.Origin, "pattern doesn't correspond to a tuple"); return; } @@ -93,30 +92,31 @@ public void CheckLinearExtendedPattern(Type type, ResolutionContext resolutionCo return; } + Contract.Assert(tupleTypeDecl.Ctors.Count == 1); + Contract.Assert(tupleTypeDecl.Ctors[0] == tupleTypeDecl.GroundingCtor); idpat.Ctor = tupleTypeDecl.GroundingCtor; - //We expect the number of arguments in the type of the matchee and the provided pattern to match, except if the pattern is a bound variable - if (udt.TypeArgs.Count != idpat.Arguments.Count) { + if (idpat.Id != tupleTypeDecl.GroundingCtor.Name) { if (idpat.Id.StartsWith(SystemModuleManager.TupleTypeCtorNamePrefix)) { - resolver.reporter.Error(MessageSource.Resolver, this.Tok, + resolver.reporter.Error(MessageSource.Resolver, this.Origin, $"the case pattern is a {idpat.Arguments.Count}-element tuple, while the match expression is a {udt.TypeArgs.Count}-element tuple"); } else { - resolver.reporter.Error(MessageSource.Resolver, this.Tok, - $"case pattern {idpat.Id} has {idpat.Arguments.Count} arguments whereas the match expression has {udt.TypeArgs.Count}"); + resolver.Reporter.Error(MessageSource.Resolver, idpat.Origin, + $"found constructor {idpat.Id} but expected a {tupleTypeDecl.Dims}-tuple"); } } var pairTP = udt.TypeArgs.Zip(idpat.Arguments, (x, y) => new Tuple(x, y)); foreach (var tp in pairTP) { - var t = resolver.PartiallyResolveTypeForMemberSelection(this.Tok, tp.Item1).NormalizeExpand(); + var t = resolver.PartiallyResolveTypeForMemberSelection(this.Origin, tp.Item1).NormalizeExpand(); tp.Item2.CheckLinearExtendedPattern(t, resolutionContext, resolver); } return; } else { // matching a datatype value if (!(this is IdPattern)) { Contract.Assert(this is LitPattern); - resolver.reporter.Error(MessageSource.Resolver, this.Tok, "Constant pattern used in place of datatype"); + resolver.reporter.Error(MessageSource.Resolver, this.Origin, "Constant pattern used in place of datatype"); return; } IdPattern idpat = (IdPattern)this; @@ -154,7 +154,7 @@ public void CheckLinearExtendedPattern(Type type, ResolutionContext resolutionCo } } else { // else applied to the wrong number of arguments - resolver.reporter.Error(MessageSource.Resolver, idpat.Tok, "constructor {0} of arity {2} is applied to {1} argument(s)", idpat.Id, (idpat.Arguments == null ? 0 : idpat.Arguments.Count), ctor.Formals.Count); + resolver.reporter.Error(MessageSource.Resolver, idpat.Origin, "constructor {0} of arity {2} is applied to {1} argument(s)", idpat.Id, (idpat.Arguments == null ? 0 : idpat.Arguments.Count), ctor.Formals.Count); } } else { /* =[4]= */ diff --git a/Source/DafnyCore/AST/Expressions/Conditional/Patterns/IdPattern.cs b/Source/DafnyCore/AST/Expressions/Conditional/Patterns/IdPattern.cs index 08000d8ff63..8992bc61164 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/Patterns/IdPattern.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/Patterns/IdPattern.cs @@ -1,47 +1,46 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.Contracts; using System.Linq; -using System.Security.AccessControl; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.Dafny; -public class IdPattern : ExtendedPattern, IHasUsages { +public class IdPattern : ExtendedPattern, IHasReferences { public bool HasParenthesis { get; } public String Id; public PreType PreType; - public Type Type; // This is the syntactic type, ExtendedPatterns dissapear during resolution. - public IVariable BoundVar { get; set; } + public Type Type; // This is the syntactic type, ExtendedPatterns disappear during resolution. + + public IVariable BoundVar { get; set; } // Only set if there are no arguments + public List Arguments; // null if just an identifier; possibly empty argument list if a constructor call public LiteralExpr ResolvedLit; // null if just an identifier [FilledInDuringResolution] public DatatypeCtor Ctor; public bool IsWildcardPattern => - Arguments == null && Id.StartsWith("_"); + Arguments == null && Id.StartsWith(WildcardString); public bool IsWildcardExact => - Arguments == null && Id == "_"; + Arguments == null && Id == WildcardString; + + public const string WildcardString = "_"; public void MakeAConstructor() { this.Arguments = new List(); } - public IdPattern(Cloner cloner, IdPattern original) : base(cloner.Tok(original.Tok), original.IsGhost) { + public IdPattern(Cloner cloner, IdPattern original) : base(cloner.Origin(original.Origin), original.IsGhost) { Id = original.Id; Arguments = original.Arguments?.Select(cloner.CloneExtendedPattern).ToList(); HasParenthesis = original.HasParenthesis; + Type = cloner.CloneType(original.Type); if (cloner.CloneResolvedFields) { BoundVar = cloner.CloneIVariable(original.BoundVar, false); - Type = original.Type; - } else { - Type = new InferredTypeProxy(); } } - public IdPattern(IToken tok, String id, List arguments, bool isGhost = false, bool hasParenthesis = false) : base(tok, isGhost) { + public IdPattern(IOrigin origin, String id, List arguments, bool isGhost = false, bool hasParenthesis = false) : base(origin, isGhost) { Contract.Requires(id != null); Contract.Requires(arguments != null); // Arguments can be empty, but shouldn't be null HasParenthesis = hasParenthesis; @@ -50,11 +49,11 @@ public IdPattern(IToken tok, String id, List arguments, bool is this.Arguments = arguments; } - public IdPattern(IToken tok, String id, Type type, List arguments, bool isGhost = false) : base(tok, isGhost) { + public IdPattern(IOrigin origin, String id, Type type, List arguments, bool isGhost = false) : base(origin, isGhost) { Contract.Requires(id != null); Contract.Requires(arguments != null); // Arguments can be empty, but shouldn't be null this.Id = id; - this.Type = type == null ? new InferredTypeProxy() : type; + this.Type = type ?? new InferredTypeProxy(); this.Arguments = arguments; this.IsGhost = isGhost; } @@ -91,10 +90,10 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti bool inPattern, bool inDisjunctivePattern) { if (inDisjunctivePattern && ResolvedLit == null && Arguments == null && !IsWildcardPattern) { - resolver.reporter.Error(MessageSource.Resolver, Tok, "Disjunctive patterns may not bind variables"); + resolver.reporter.Error(MessageSource.Resolver, Origin, "Disjunctive patterns may not bind variables"); } - resolver.ResolveType(Tok, Type, resolutionContext, ResolveTypeOptionEnum.InferTypeProxies, null); + resolver.ResolveType(Origin, Type, resolutionContext, ResolveTypeOptionEnum.InferTypeProxies, null); if (ResolvedLit != null) { // we're done @@ -105,17 +104,18 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti } if (inStatementContext) { - var localVariable = new LocalVariable(RangeToken, Id, null, isGhost) { + var localVariable = new LocalVariable(Origin, Id, null, isGhost) { type = Type }; BoundVar = localVariable; } else { - var boundVar = new BoundVar(Tok, Id, Type); - boundVar.IsGhost = isGhost; + var boundVar = new BoundVar(Origin, Id, Type) { + IsGhost = isGhost + }; BoundVar = boundVar; } - resolver.ConstrainSubtypeRelation(Type, sourceType, Tok, + resolver.ConstrainSubtypeRelation(Type, sourceType, Origin, "match source type '{1}' not assignable to bound variable (of type '{0}')", Type, sourceType); resolver.scope.Push(Id, BoundVar); @@ -130,32 +130,30 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti } } - public IEnumerable GetResolvedDeclarations() { - return new IDeclarationOrUsage[] { Ctor }.Where(x => x != null); + public IEnumerable GetReferences() { + return Ctor == null ? Enumerable.Empty() : new[] { new Reference(StartToken, Ctor) }; } - public IToken NameToken => Tok; - public void CheckLinearVarPattern(Type type, ResolutionContext resolutionContext, ModuleResolver resolver) { if (Arguments != null) { if (Id == SystemModuleManager.TupleTypeCtorName(1)) { - resolver.reporter.Error(MessageSource.Resolver, this.Tok, "parentheses are not allowed around a pattern"); + resolver.reporter.Error(MessageSource.Resolver, this.Origin, "parentheses are not allowed around a pattern"); } else { - resolver.reporter.Error(MessageSource.Resolver, this.Tok, "member {0} does not exist in type {1}", this.Id, type); + resolver.reporter.Error(MessageSource.Resolver, this.Origin, "member {0} does not exist in type {1}", this.Id, type); } return; } if (resolver.scope.FindInCurrentScope(this.Id) != null) { - resolver.reporter.Error(MessageSource.Resolver, this.Tok, "Duplicate parameter name: {0}", this.Id); + resolver.reporter.Error(MessageSource.Resolver, this.Origin, "Duplicate parameter name: {0}", this.Id); } else if (IsWildcardPattern) { // Wildcard, ignore return; } else { - NameSegment e = new NameSegment(this.Tok, this.Id, null); + NameSegment e = new NameSegment(this.Origin, this.Id, null); resolver.ResolveNameSegment(e, true, null, resolutionContext, false, false); if (e.ResolvedExpression == null) { - resolver.ScopePushAndReport(resolver.scope, new BoundVar(this.Tok, this.Id, type), "parameter"); + resolver.ScopePushAndReport(resolver.scope, new BoundVar(this.Origin, this.Id, type), "parameter"); } else { // finds in full scope, not just current scope if (e.Resolved is MemberSelectExpr mse) { @@ -167,19 +165,19 @@ public void CheckLinearVarPattern(Type type, ResolutionContext resolutionContext // OK - type is correct } else { // may well be a proxy so add a type constraint - resolver.ConstrainSubtypeRelation(e.ResolvedExpression.Type, type, this.Tok, + resolver.ConstrainSubtypeRelation(e.ResolvedExpression.Type, type, this.Origin, "the type of the pattern ({0}) does not agree with the match expression ({1})", e.ResolvedExpression.Type, type); } } else { - resolver.reporter.Error(MessageSource.Resolver, this.Tok, "{0} is not initialized as a constant literal", this.Id); - resolver.ScopePushAndReport(resolver.scope, new BoundVar(this.Tok, this.Id, type), "parameter"); + resolver.reporter.Error(MessageSource.Resolver, this.Origin, "{0} is not initialized as a constant literal", this.Id); + resolver.ScopePushAndReport(resolver.scope, new BoundVar(this.Origin, this.Id, type), "parameter"); } } else { // Not a static const, so just a variable - resolver.ScopePushAndReport(resolver.scope, new BoundVar(this.Tok, this.Id, type), "parameter"); + resolver.ScopePushAndReport(resolver.scope, new BoundVar(this.Origin, this.Id, type), "parameter"); } } else { - resolver.ScopePushAndReport(resolver.scope, new BoundVar(this.Tok, this.Id, type), "parameter"); + resolver.ScopePushAndReport(resolver.scope, new BoundVar(this.Origin, this.Id, type), "parameter"); } } } diff --git a/Source/DafnyCore/AST/Expressions/Conditional/Patterns/LitPattern.cs b/Source/DafnyCore/AST/Expressions/Conditional/Patterns/LitPattern.cs index a5e2e64584f..652521f13e6 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/Patterns/LitPattern.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/Patterns/LitPattern.cs @@ -39,11 +39,11 @@ public LiteralExpr OptimisticallyDesugaredLit { if (OrigLit is NegationExpression neg) { var lit = (LiteralExpr)neg.E; if (lit.Value is BaseTypes.BigDec d) { - optimisticallyDesugaredLit = new LiteralExpr(neg.tok, -d); + optimisticallyDesugaredLit = new LiteralExpr(neg.Origin, -d); } else { var n = (BigInteger)lit.Value; - var tok = new Token(neg.tok.line, neg.tok.col) { - Uri = neg.tok.Uri, + var tok = new Token(neg.Origin.line, neg.Origin.col) { + Uri = neg.Origin.Uri, val = "-0" }; optimisticallyDesugaredLit = new LiteralExpr(tok, -n); @@ -56,7 +56,7 @@ public LiteralExpr OptimisticallyDesugaredLit { } } - public LitPattern(IToken tok, Expression lit, bool isGhost = false) : base(tok, isGhost) { + public LitPattern(IOrigin origin, Expression lit, bool isGhost = false) : base(origin, isGhost) { Contract.Requires(lit is LiteralExpr || lit is NegationExpression); this.OrigLit = lit; } @@ -81,7 +81,7 @@ public override void Resolve(ModuleResolver resolver, var literal = OptimisticallyDesugaredLit; resolver.ResolveExpression(literal, resolutionContext); - resolver.AddAssignableConstraint(literal.tok, sourceType, literal.Type, + resolver.AddAssignableConstraint(literal.Origin, sourceType, literal.Type, "literal expression in case (of type '{1}') not assignable to match source type '{0}'"); } } diff --git a/Source/DafnyCore/AST/Expressions/Conditional/TernaryExpr.cs b/Source/DafnyCore/AST/Expressions/Conditional/TernaryExpr.cs index 4483e88214d..fc193a52d09 100644 --- a/Source/DafnyCore/AST/Expressions/Conditional/TernaryExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Conditional/TernaryExpr.cs @@ -8,8 +8,7 @@ public class TernaryExpr : Expression, ICloneable { public readonly Expression E0; public readonly Expression E1; public readonly Expression E2; - public enum Opcode { /*SOON: IfOp,*/ PrefixEqOp, PrefixNeqOp } - public static readonly bool PrefixEqUsesNat = false; // "k" is either a "nat" or an "ORDINAL" + public enum Opcode { PrefixEqOp, PrefixNeqOp } public TernaryExpr(Cloner cloner, TernaryExpr original) : base(cloner, original) { Op = original.Op; @@ -18,9 +17,9 @@ public TernaryExpr(Cloner cloner, TernaryExpr original) : base(cloner, original) E2 = cloner.CloneExpr(original.E2); } - public TernaryExpr(IToken tok, Opcode op, Expression e0, Expression e1, Expression e2) - : base(tok) { - Contract.Requires(tok != null); + public TernaryExpr(IOrigin origin, Opcode op, Expression e0, Expression e1, Expression e2) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(e0 != null); Contract.Requires(e1 != null); Contract.Requires(e2 != null); diff --git a/Source/DafnyCore/AST/Expressions/Datatypes/DatatypeUpdateExpr.cs b/Source/DafnyCore/AST/Expressions/Datatypes/DatatypeUpdateExpr.cs index 6528ea4534b..1568b19b5ab 100644 --- a/Source/DafnyCore/AST/Expressions/Datatypes/DatatypeUpdateExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Datatypes/DatatypeUpdateExpr.cs @@ -5,9 +5,9 @@ namespace Microsoft.Dafny; -public class DatatypeUpdateExpr : ConcreteSyntaxExpression, IHasUsages, ICloneable { +public class DatatypeUpdateExpr : ConcreteSyntaxExpression, IHasReferences, ICloneable { public readonly Expression Root; - public readonly List> Updates; + public readonly List> Updates; [FilledInDuringResolution] public List Members; [FilledInDuringResolution] public List LegalSourceConstructors; [FilledInDuringResolution] public bool InCompiledContext; @@ -19,20 +19,24 @@ public DatatypeUpdateExpr Clone(Cloner cloner) { public DatatypeUpdateExpr(Cloner cloner, DatatypeUpdateExpr original) : base(cloner, original) { Root = cloner.CloneExpr(original.Root); - Updates = original.Updates.Select(t => Tuple.Create(cloner.Tok((IToken)t.Item1), t.Item2, cloner.CloneExpr(t.Item3))) + Updates = original.Updates.Select(t => Tuple.Create(cloner.Origin(t.Item1), t.Item2, cloner.CloneExpr(t.Item3))) .ToList(); if (cloner.CloneResolvedFields) { Members = original.Members; LegalSourceConstructors = original.LegalSourceConstructors; InCompiledContext = original.InCompiledContext; - ResolvedCompiledExpression = cloner.CloneExpr(original.ResolvedCompiledExpression); + if (original.ResolvedExpression == original.ResolvedCompiledExpression) { + ResolvedCompiledExpression = ResolvedExpression; + } else { + ResolvedCompiledExpression = cloner.CloneExpr(original.ResolvedCompiledExpression); + } } } - public DatatypeUpdateExpr(IToken tok, Expression root, List> updates) - : base(tok) { - Contract.Requires(tok != null); + public DatatypeUpdateExpr(IOrigin origin, Expression root, List> updates) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(root != null); Contract.Requires(updates != null); Contract.Requires(updates.Count != 0); @@ -43,24 +47,27 @@ public DatatypeUpdateExpr(IToken tok, Expression root, List SubExpressions { get { if (ResolvedExpression == null) { + Contract.Assert(ResolvedCompiledExpression == null); foreach (var preResolved in PreResolveSubExpressions) { - yield return preResolved; } } else { foreach (var e in base.SubExpressions) { yield return e; } + if (ResolvedExpression != ResolvedCompiledExpression) { + yield return ResolvedCompiledExpression; + } } } } - public IEnumerable GetResolvedDeclarations() { - return LegalSourceConstructors; + public IEnumerable GetReferences() { + return LegalSourceConstructors == null ? Enumerable.Empty() + : Updates.Zip(LegalSourceConstructors).Select(t => + new Reference(t.First.Item1, t.Second.Formals.Find(f => f.Name == t.First.Item2))); } - public IToken NameToken => tok; - public override IEnumerable PreResolveSubExpressions { get { yield return Root; diff --git a/Source/DafnyCore/AST/Expressions/Datatypes/DatatypeValue.cs b/Source/DafnyCore/AST/Expressions/Datatypes/DatatypeValue.cs index 9cd5476536b..9fd6c491373 100644 --- a/Source/DafnyCore/AST/Expressions/Datatypes/DatatypeValue.cs +++ b/Source/DafnyCore/AST/Expressions/Datatypes/DatatypeValue.cs @@ -4,7 +4,7 @@ namespace Microsoft.Dafny; -public class DatatypeValue : Expression, IHasUsages, ICloneable, ICanFormat { +public class DatatypeValue : Expression, IHasReferences, ICloneable, ICanFormat { public readonly string DatatypeName; public readonly string MemberName; public readonly ActualBindings Bindings; @@ -41,10 +41,10 @@ public DatatypeValue(Cloner cloner, DatatypeValue original) : base(cloner, origi } } - public DatatypeValue(IToken tok, string datatypeName, string memberName, [Captured] List arguments) - : base(tok) { + public DatatypeValue(IOrigin origin, string datatypeName, string memberName, [Captured] List arguments) + : base(origin) { Contract.Requires(cce.NonNullElements(arguments)); - Contract.Requires(tok != null); + Contract.Requires(origin != null); Contract.Requires(datatypeName != null); Contract.Requires(memberName != null); this.DatatypeName = datatypeName; @@ -56,19 +56,18 @@ public DatatypeValue(IToken tok, string datatypeName, string memberName, [Captur /// This constructor is intended to be used when constructing a resolved DatatypeValue. The "args" are expected /// to be already resolved, and are all given positionally. /// - public DatatypeValue(IToken tok, string datatypeName, string memberName, List arguments) - : this(tok, datatypeName, memberName, arguments.ConvertAll(e => new ActualBinding(null, e))) { + public DatatypeValue(IOrigin origin, string datatypeName, string memberName, List arguments) + : this(origin, datatypeName, memberName, arguments.ConvertAll(e => new ActualBinding(null, e))) { Bindings.AcceptArgumentExpressionsAsExactParameterList(); } public override IEnumerable SubExpressions => Arguments ?? Enumerable.Empty(); - public IEnumerable GetResolvedDeclarations() { - return Enumerable.Repeat(Ctor, 1); + public IEnumerable GetReferences() { + return Enumerable.Repeat(new Reference(Origin, Ctor), 1); } - public IToken NameToken => tok; public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { formatter.SetMethodLikeIndent(StartToken, OwnedTokens, indentBefore); return true; diff --git a/Source/DafnyCore/AST/Expressions/DefaultValueExpression.cs b/Source/DafnyCore/AST/Expressions/DefaultValueExpression.cs index 5df4f73ee03..446b200ee4a 100644 --- a/Source/DafnyCore/AST/Expressions/DefaultValueExpression.cs +++ b/Source/DafnyCore/AST/Expressions/DefaultValueExpression.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; namespace Microsoft.Dafny; @@ -22,63 +23,121 @@ namespace Microsoft.Dafny; /// writing, a change to a separate type has shown to be more hassle than the need for special /// attention to DefaultValueExpression's in some places. /// -public class DefaultValueExpression : ConcreteSyntaxExpression, ICloneable { - public readonly Formal Formal; - public readonly Expression Receiver; - public readonly Dictionary SubstMap; - public readonly Dictionary TypeMap; +public abstract class DefaultValueExpression : ConcreteSyntaxExpression { + private readonly Formal formal; + private readonly Expression receiver; + private readonly Dictionary substMap; - public DefaultValueExpression(IToken tok, Formal formal, - Expression/*?*/ receiver, Dictionary substMap, Dictionary typeMap) - : base(tok) { - Contract.Requires(tok != null); + protected abstract Dictionary GetTypeMap(); + + public enum WorkProgress { BeingVisited, Done } + + protected DefaultValueExpression(IOrigin origin, Formal formal, Expression/*?*/ receiver, Dictionary substMap) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(formal != null); Contract.Requires(formal.DefaultValue != null); Contract.Requires(substMap != null); - Contract.Requires(typeMap != null); - Formal = formal; - Receiver = receiver; - SubstMap = substMap; - TypeMap = typeMap; - Type = formal.Type.Subst(typeMap); - RangeToken = new RangeToken(tok, tok); + this.formal = formal; + this.receiver = receiver; + this.substMap = substMap; } - public DefaultValueExpression(Cloner cloner, DefaultValueExpression original) : base(cloner, original) { + protected DefaultValueExpression(Cloner cloner, DefaultValueExpression original) : base(cloner, original) { if (!cloner.CloneResolvedFields) { throw new InvalidOperationException("DefaultValueExpression is created during resolution"); } - Formal = cloner.CloneFormal(original.Formal, true); - Receiver = cloner.CloneExpr(original.Receiver); - SubstMap = original.SubstMap; - TypeMap = original.TypeMap; + formal = cloner.CloneFormal(original.formal, true); + receiver = cloner.CloneExpr(original.receiver); + substMap = original.substMap; + } + + public void FillIn(ModuleResolver resolver, Dictionary visited) { + if (visited.TryGetValue(this, out var p)) { + if (p == WorkProgress.Done) { + Contract.Assert(this.ResolvedExpression != null); + } else { + // there is a cycle + resolver.reporter.Error(MessageSource.Resolver, this, + "default-valued expressions are cyclicly dependent; this is not allowed, since it would cause infinite expansion"); + // nevertheless, to avoid any issues in the resolver, fill in the .ResolvedExpression field with something + this.ResolvedExpression = Expression.CreateBoolLiteral(this.Origin, false); + } + return; + } + Contract.Assert(this.ResolvedExpression == null); + + visited.Add(this, WorkProgress.BeingVisited); + var typeMap = GetTypeMap(); + var s = new DefaultValueSubstituter(resolver, visited, this.receiver, this.substMap, typeMap); + this.ResolvedExpression = s.Substitute(this.formal.DefaultValue); + visited[this] = WorkProgress.Done; } - public DefaultValueExpression Clone(Cloner cloner) { - return new DefaultValueExpression(cloner, this); + class DefaultValueSubstituter : Substituter { + private readonly ModuleResolver resolver; + private readonly Dictionary visited; + public DefaultValueSubstituter(ModuleResolver resolver, Dictionary visited, + Expression /*?*/ receiverReplacement, Dictionary substMap, Dictionary typeMap) + : base(receiverReplacement, substMap, typeMap) { + Contract.Requires(resolver != null); + Contract.Requires(visited != null); + this.resolver = resolver; + this.visited = visited; + } + + public override Expression Substitute(Expression expr) { + if (expr is DefaultValueExpression dve) { + dve.FillIn(resolver, visited); + Contract.Assert(dve.ResolvedExpression != null); // postcondition of FillIn + } + return base.Substitute(expr); + } } } -/// TODO: This class is a bit sketchy. It should probably be used to replace DefaultValueExpression in some way. -/// -public class DefaultValueExpressionPreType : ConcreteSyntaxExpression { - public readonly Formal Formal; - public readonly Expression Receiver; - public readonly Dictionary SubstMap; - public readonly Dictionary PreTypeMap; +public class DefaultValueExpressionType : DefaultValueExpression, ICloneable { + private readonly Dictionary typeMap; + + public DefaultValueExpressionType(IOrigin origin, Formal formal, + Expression/*?*/ receiver, Dictionary substMap, Dictionary typeMap) + : base(origin, formal, receiver, substMap) { + this.typeMap = typeMap; + } + + public DefaultValueExpressionType(Cloner cloner, DefaultValueExpressionType original) : base(cloner, original) { + typeMap = original.typeMap; + } - public DefaultValueExpressionPreType(IToken tok, Formal formal, + public DefaultValueExpressionType Clone(Cloner cloner) { + return new DefaultValueExpressionType(cloner, this); + } + + protected override Dictionary GetTypeMap() { + return typeMap; + } +} + +public class DefaultValueExpressionPreType : DefaultValueExpression, ICloneable { + private readonly Dictionary preTypeMap; + + public DefaultValueExpressionPreType(IOrigin origin, Formal formal, Expression/*?*/ receiver, Dictionary substMap, Dictionary preTypeMap) - : base(tok) { - Contract.Requires(tok != null); - Contract.Requires(formal != null); - Contract.Requires(formal.DefaultValue != null); - Contract.Requires(substMap != null); - Contract.Requires(preTypeMap != null); - Formal = formal; - Receiver = receiver; - SubstMap = substMap; - PreTypeMap = preTypeMap; - PreType = formal.PreType.Substitute(preTypeMap); + : base(origin, formal, receiver, substMap) { + this.preTypeMap = preTypeMap; + } + + public DefaultValueExpressionPreType(Cloner cloner, DefaultValueExpressionPreType original) : base(cloner, original) { + preTypeMap = original.preTypeMap; + } + + public DefaultValueExpressionPreType Clone(Cloner cloner) { + return new DefaultValueExpressionPreType(cloner, this); + } + + protected override Dictionary GetTypeMap() { + return preTypeMap.ToDictionary( + x => x.Key, + x => PreType2TypeUtil.PreType2FixedType(x.Value)); } -} \ No newline at end of file +} diff --git a/Source/DafnyCore/AST/Expressions/Expression.cs b/Source/DafnyCore/AST/Expressions/Expression.cs index aa652b3dc2e..74e9e6ac2c4 100644 --- a/Source/DafnyCore/AST/Expressions/Expression.cs +++ b/Source/DafnyCore/AST/Expressions/Expression.cs @@ -1,15 +1,15 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; using System.Numerics; -using Microsoft.Boogie; namespace Microsoft.Dafny; -public abstract class Expression : TokenNode { +public abstract class Expression : NodeWithComputedRange { [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(tok != null); + Contract.Invariant(Origin != null); } [System.Diagnostics.Contracts.Pure] @@ -50,7 +50,7 @@ public virtual IEnumerable TerminalExpressions { public Type Type { get { Contract.Ensures(type != null || Contract.Result() == null); // useful in conjunction with postcondition of constructor - return type == null ? null : type.Normalize(); + return type?.Normalize(); } set { Contract.Requires(!WasResolved()); // set it only once @@ -60,6 +60,22 @@ public Type Type { type = value.Normalize(); } } + + /// + /// The new type inference includes a "type refinement" phase, which determines the best subset types for a program. This phase works + /// by refining (mutating in the direction from bottom, meaning un ansatisfiable constraint, to top, meaning no constraint) types in place, + /// using "TypeRefinementWrapper" type proxies. During that phase, it is necessary to obtain the + /// un-normalized type stored in each AST node, which is what the "UnnormalizedType" property does. This property should only be used + /// during the type refinement phase. After type inference is complete, use ".Type" instead. + /// + public Type UnnormalizedType { + get { + return type; + } + set { + type = value; + } + } /// /// This method can be used when .Type has been found to be erroneous and its current value /// would be unexpected by the rest of the resolver. This method then sets .Type to a neutral @@ -77,20 +93,15 @@ public void DebugTest_ChangeType(Type ty) { } #endif - public Expression(IToken tok) { - Contract.Requires(tok != null); + protected Expression(IOrigin origin) : base(origin) { + Contract.Requires(origin != null); Contract.Ensures(type == null); // we would have liked to have written Type==null, but that's not admissible or provable - - this.tok = tok; } - protected Expression(Cloner cloner, Expression original) { - - tok = cloner.Tok(original.tok); - RangeToken = cloner.Range(original.RangeToken); - + protected Expression(Cloner cloner, Expression original) : base(cloner, original) { if (cloner.CloneResolvedFields && original.Type != null) { Type = original.Type; + PreType = original.PreType; } } @@ -111,6 +122,23 @@ public virtual IEnumerable SubExpressions { get { yield break; } } + public IEnumerable DescendantsAndSelf { + get { + Stack todo = new(); + List result = new(); + todo.Push(this); + while (todo.Any()) { + var current = todo.Pop(); + result.Add(current); + foreach (var child in current.SubExpressions) { + todo.Push(child); + } + } + + return result; + } + } + /// /// Returns the list of types that appear in this expression proper (that is, not including types that /// may appear in subexpressions). Types occurring in substatements of the expression are not included. @@ -120,8 +148,32 @@ public virtual IEnumerable ComponentTypes { get { yield break; } } - public virtual bool IsImplicit { - get { return false; } + public virtual bool IsImplicit => false; + + public static IEnumerable ConjunctsWithLetsOnOutside(Expression expr) { + foreach (var conjunct in Conjuncts(expr)) { + if (conjunct is LetExpr { Exact: true } letExpr) { + foreach (var letBodyConjunct in ConjunctsWithLetsOnOutside(letExpr.Body)) { + yield return new LetExpr(letExpr.Origin, letExpr.LHSs, letExpr.RHSs, letBodyConjunct, letExpr.Exact, letExpr.Attributes) { + Type = letExpr.Type + }; + } + } else { + yield return conjunct; + } + } + } + + /// + /// Return the negation of each of the expressions in "expressions". + /// If there is just one expression in "expressions", then use the given token "tok" for the negation. + /// Otherwise, use the token from each expression. + /// + static IEnumerable NegateEach(IOrigin tok, IEnumerable expressions) { + var exprs = expressions.ToList(); + foreach (Expression e in exprs) { + yield return Expression.CreateNot(exprs.Count == 1 ? tok : e.Origin, e); + } } public static IEnumerable Conjuncts(Expression expr) { @@ -130,9 +182,9 @@ public static IEnumerable Conjuncts(Expression expr) { Contract.Ensures(cce.NonNullElements(Contract.Result>())); expr = StripParens(expr); - if (expr is UnaryOpExpr unary && unary.Op == UnaryOpExpr.Opcode.Not) { - foreach (Expression e in Disjuncts(unary.E)) { - yield return Expression.CreateNot(e.tok, e); + if (expr is UnaryOpExpr { Op: UnaryOpExpr.Opcode.Not } unary) { + foreach (Expression e in NegateEach(expr.Origin, Disjuncts(unary.E))) { + yield return e; } yield break; @@ -157,26 +209,26 @@ public static IEnumerable Disjuncts(Expression expr) { Contract.Ensures(cce.NonNullElements(Contract.Result>())); expr = StripParens(expr); - if (expr is UnaryOpExpr unary && unary.Op == UnaryOpExpr.Opcode.Not) { - foreach (Expression e in Conjuncts(unary.E)) { - yield return Expression.CreateNot(e.tok, e); + if (expr is UnaryOpExpr { Op: UnaryOpExpr.Opcode.Not } unary) { + foreach (Expression e in NegateEach(expr.Origin, Conjuncts(unary.E))) { + yield return e; } yield break; } else if (expr is BinaryExpr bin) { if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Or) { - foreach (Expression e in Conjuncts(bin.E0)) { + foreach (Expression e in Disjuncts(bin.E0)) { yield return e; } - foreach (Expression e in Conjuncts(bin.E1)) { + foreach (Expression e in Disjuncts(bin.E1)) { yield return e; } yield break; } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp) { foreach (Expression e in Conjuncts(bin.E0)) { - yield return Expression.CreateNot(e.tok, e); + yield return Expression.CreateNot(e.Origin, e); } - foreach (Expression e in Conjuncts(bin.E1)) { + foreach (Expression e in Disjuncts(bin.E1)) { yield return e; } yield break; @@ -196,7 +248,7 @@ public static Expression CreateAdd(Expression e0, Expression e1) { (e0.Type.IsNumericBased(Type.NumericPersuasion.Int) && e1.Type.IsNumericBased(Type.NumericPersuasion.Int)) || (e0.Type.IsNumericBased(Type.NumericPersuasion.Real) && e1.Type.IsNumericBased(Type.NumericPersuasion.Real))); Contract.Ensures(Contract.Result() != null); - var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Add, e0, e1); + var s = new BinaryExpr(e0.Origin, BinaryExpr.Opcode.Add, e0, e1); s.ResolvedOp = BinaryExpr.ResolvedOpcode.Add; // resolve here s.Type = e0.Type.NormalizeExpand(); // resolve here return s; @@ -212,7 +264,7 @@ public static Expression CreateMul(Expression e0, Expression e1) { (e0.Type.IsNumericBased(Type.NumericPersuasion.Int) && e1.Type.IsNumericBased(Type.NumericPersuasion.Int)) || (e0.Type.IsNumericBased(Type.NumericPersuasion.Real) && e1.Type.IsNumericBased(Type.NumericPersuasion.Real))); Contract.Ensures(Contract.Result() != null); - var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Mul, e0, e1); + var s = new BinaryExpr(e0.Origin, BinaryExpr.Opcode.Mul, e0, e1); s.ResolvedOp = BinaryExpr.ResolvedOpcode.Mul; // resolve here s.Type = e0.Type.NormalizeExpand(); // resolve here return s; @@ -248,7 +300,7 @@ public static Expression CreateSubtract_TypeConvert(Expression e0, Expression e1 private static Expression CastIfNeeded(Expression expr, Type toType) { if (!expr.Type.Equals(toType)) { - var cast = new ConversionExpr(expr.tok, expr, toType); + var cast = new ConversionExpr(expr.Origin, expr, toType); cast.Type = toType; return cast; } else { @@ -269,7 +321,7 @@ public static Expression CreateSubtract(Expression e0, Expression e1) { (e0.Type.IsNumericBased(Type.NumericPersuasion.Real) && e1.Type.IsNumericBased(Type.NumericPersuasion.Real)) || (e0.Type.IsBigOrdinalType && e1.Type.IsBigOrdinalType)); Contract.Ensures(Contract.Result() != null); - var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Sub, e0, e1); + var s = new BinaryExpr(e0.Origin, BinaryExpr.Opcode.Sub, e0, e1); s.ResolvedOp = BinaryExpr.ResolvedOpcode.Sub; // resolve here s.Type = e0.Type.NormalizeExpand(); // resolve here (and it's important to remove any constraints) return s; @@ -289,7 +341,7 @@ public static Expression CreateSetDifference(Expression e0, Expression e1) { if (LiteralExpr.IsEmptySet(e0) || LiteralExpr.IsEmptySet(e1)) { return e0; } - var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Sub, e0, e1) { + var s = new BinaryExpr(e0.Origin, BinaryExpr.Opcode.Sub, e0, e1) { ResolvedOp = BinaryExpr.ResolvedOpcode.SetDifference, Type = e0.Type.NormalizeExpand() // important to remove any constraints }; @@ -310,7 +362,7 @@ public static Expression CreateMultisetDifference(Expression e0, Expression e1) if (LiteralExpr.IsEmptyMultiset(e0) || LiteralExpr.IsEmptyMultiset(e1)) { return e0; } - var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Sub, e0, e1) { + var s = new BinaryExpr(e0.Origin, BinaryExpr.Opcode.Sub, e0, e1) { ResolvedOp = BinaryExpr.ResolvedOpcode.MultiSetDifference, Type = e0.Type.NormalizeExpand() // important to remove any constraints }; @@ -325,7 +377,7 @@ public static Expression CreateCardinality(Expression e, SystemModuleManager sys Contract.Requires(e.Type != null); Contract.Requires(e.Type.AsSetType != null || e.Type.AsMultiSetType != null || e.Type.AsSeqType != null); Contract.Ensures(Contract.Result() != null); - var s = new UnaryOpExpr(e.tok, UnaryOpExpr.Opcode.Cardinality, e) { + var s = new UnaryOpExpr(e.Origin, UnaryOpExpr.Opcode.Cardinality, e) { Type = systemModuleManager.Nat() }; return s; @@ -343,7 +395,7 @@ public static Expression CreateIncrement(Expression e, int n) { if (n == 0) { return e; } - var nn = CreateIntLiteral(e.tok, n); + var nn = CreateIntLiteral(e.Origin, n); return CreateAdd(e, nn); } @@ -358,29 +410,38 @@ public static Expression CreateDecrement(Expression e, int n, Type ty = null) { if (n == 0) { return e; } - var nn = CreateIntLiteral(e.tok, n, ty); + var nn = CreateIntLiteralNonnegative(e.Origin, n, ty); return CreateSubtract(e, nn); } + /// + /// Create a resolved expression of the form "n" when n is nonnegative + /// + public static LiteralExpr CreateIntLiteralNonnegative(IOrigin tok, int n, Type ty = null) { + Contract.Requires(tok != null); + Contract.Requires(0 <= n); + var nn = new LiteralExpr(tok, n); + nn.Type = ty ?? Type.Int; + return nn; + } + /// /// Create a resolved expression of the form "n" /// - public static Expression CreateIntLiteral(IToken tok, int n, Type ty = null) { + public static Expression CreateIntLiteral(IOrigin tok, int n, Type ty = null) { Contract.Requires(tok != null); Contract.Requires(n != int.MinValue); if (0 <= n) { - var nn = new LiteralExpr(tok, n); - nn.Type = ty ?? Type.Int; - return nn; + return CreateIntLiteralNonnegative(tok, n, ty); } else { - return CreateDecrement(CreateIntLiteral(tok, 0, ty), -n, ty); + return CreateDecrement(CreateIntLiteralNonnegative(tok, 0, ty), -n, ty); } } /// /// Create a resolved expression of the form "x" /// - public static Expression CreateRealLiteral(IToken tok, BaseTypes.BigDec x) { + public static Expression CreateRealLiteral(IOrigin tok, BaseTypes.BigDec x) { Contract.Requires(tok != null); var nn = new LiteralExpr(tok, x); nn.Type = Type.Real; @@ -390,7 +451,7 @@ public static Expression CreateRealLiteral(IToken tok, BaseTypes.BigDec x) { /// /// Create a resolved expression of the form "n", for either type "int" or type "ORDINAL". /// - public static Expression CreateNatLiteral(IToken tok, int n, Type ty) { + public static Expression CreateNatLiteral(IOrigin tok, int n, Type ty) { Contract.Requires(tok != null); Contract.Requires(0 <= n); Contract.Requires(ty.IsNumericBased(Type.NumericPersuasion.Int) || ty is BigOrdinalType); @@ -402,21 +463,23 @@ public static Expression CreateNatLiteral(IToken tok, int n, Type ty) { /// /// Create a resolved expression for a bool b /// - public static LiteralExpr CreateBoolLiteral(IToken tok, bool b) { + public static LiteralExpr CreateBoolLiteral(IOrigin tok, bool b) { Contract.Requires(tok != null); - var lit = new LiteralExpr(tok, b); - lit.Type = Type.Bool; // resolve here + var lit = new LiteralExpr(tok, b) { + Type = Type.Bool + }; return lit; } /// /// Create a resolved expression for a string s /// - public static LiteralExpr CreateStringLiteral(IToken tok, string s) { + public static LiteralExpr CreateStringLiteral(IOrigin tok, string s) { Contract.Requires(tok != null); Contract.Requires(s != null); - var lit = new StringLiteralExpr(tok, s, true); - lit.Type = new SeqType(new CharType()); // resolve here + var lit = new StringLiteralExpr(tok, s, true) { + Type = new SeqType(new CharType()) + }; return lit; } @@ -426,8 +489,7 @@ public static LiteralExpr CreateStringLiteral(IToken tok, string s) { /// public static Expression StripParens(Expression expr) { while (true) { - var e = expr as ParensExpression; - if (e == null) { + if (expr is not ParensExpression e) { return expr; } expr = e.E; @@ -479,11 +541,11 @@ public static bool IsBoolLiteral(Expression expr, out bool value) { /// public static bool IsIntLiteral(Expression expr, out BigInteger value) { Contract.Requires(expr != null); - var e = StripParens(expr) as LiteralExpr; - if (e != null && e.Value is int x) { + var e = StripParensAndCasts(expr) as LiteralExpr; + if (e is { Value: int x }) { value = new BigInteger(x); return true; - } else if (e != null && e.Value is BigInteger xx) { + } else if (e is { Value: BigInteger xx }) { value = xx; return true; } else { @@ -506,11 +568,11 @@ public static bool IsEmptySetOrMultiset(Expression expr) { /// /// Create a resolved ParensExpression around a given resolved expression "e". /// - public static Expression CreateParensExpression(IToken tok, Expression e) { + public static Expression CreateParensExpression(IOrigin tok, Expression e) { return new ParensExpression(tok, e) { Type = e.Type, ResolvedExpression = e }; } - public static Expression CreateNot(IToken tok, Expression e) { + public static Expression CreateNot(IOrigin tok, Expression e) { Contract.Requires(tok != null); Contract.Requires(e != null && e.Type != null && e.Type.IsBoolType); @@ -556,7 +618,7 @@ public static Expression CreateNot(IToken tok, Expression e) { break; } if (negatedOp != BinaryExpr.ResolvedOpcode.Add) { - return new BinaryExpr(bin.tok, BinaryExpr.ResolvedOp2SyntacticOp(negatedOp), bin.E0, bin.E1) { + return new BinaryExpr(bin.Origin, BinaryExpr.ResolvedOp2SyntacticOp(negatedOp), bin.E0, bin.E1) { ResolvedOp = negatedOp, Type = bin.Type }; @@ -582,7 +644,7 @@ public static Expression CreateLess(Expression e0, Expression e1) { (e0.Type.IsCharType && e1.Type.IsCharType) || (e0.Type.IsBigOrdinalType && e1.Type.IsBigOrdinalType)); Contract.Ensures(Contract.Result() != null); - return new BinaryExpr(e0.tok, BinaryExpr.Opcode.Lt, e0, e1) { + return new BinaryExpr(e0.Origin, BinaryExpr.Opcode.Lt, e0, e1) { ResolvedOp = e0.Type.IsCharType ? BinaryExpr.ResolvedOpcode.LtChar : BinaryExpr.ResolvedOpcode.Lt, Type = Type.Bool }; @@ -602,7 +664,7 @@ public static Expression CreateAtMost(Expression e0, Expression e1) { (e0.Type.IsCharType && e1.Type.IsCharType) || (e0.Type.IsBigOrdinalType && e1.Type.IsBigOrdinalType)); Contract.Ensures(Contract.Result() != null); - return new BinaryExpr(e0.tok, BinaryExpr.Opcode.Le, e0, e1) { + return new BinaryExpr(e0.Origin, BinaryExpr.Opcode.Le, e0, e1) { ResolvedOp = e0.Type.IsCharType ? BinaryExpr.ResolvedOpcode.LeChar : BinaryExpr.ResolvedOpcode.Le, Type = Type.Bool }; @@ -612,7 +674,7 @@ public static Expression CreateEq(Expression e0, Expression e1, Type ty) { Contract.Requires(e0 != null); Contract.Requires(e1 != null); Contract.Requires(ty != null); - var eq = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Eq, e0, e1); + var eq = new BinaryExpr(e0.Origin, BinaryExpr.Opcode.Eq, e0, e1); if (ty is SetType) { eq.ResolvedOp = BinaryExpr.ResolvedOpcode.SetEq; } else if (ty is SeqType) { @@ -641,7 +703,7 @@ public static Expression CreateAnd(Expression a, Expression b, bool allowSimplif } else if (allowSimplification && LiteralExpr.IsTrue(b)) { return a; } else { - var and = new BinaryExpr(a.tok, BinaryExpr.Opcode.And, a, b); + var and = new BinaryExpr(a.Origin, BinaryExpr.Opcode.And, a, b); and.ResolvedOp = BinaryExpr.ResolvedOpcode.And; // resolve here and.Type = Type.Bool; // resolve here return and; @@ -659,7 +721,7 @@ public static Expression CreateImplies(Expression a, Expression b, bool allowSim if (allowSimplification && (LiteralExpr.IsTrue(a) || LiteralExpr.IsTrue(b))) { return b; } else { - var imp = new BinaryExpr(a.tok, BinaryExpr.Opcode.Imp, a, b); + var imp = new BinaryExpr(a.Origin, BinaryExpr.Opcode.Imp, a, b); imp.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp; // resolve here imp.Type = Type.Bool; // resolve here return imp; @@ -679,7 +741,7 @@ public static Expression CreateOr(Expression a, Expression b, bool allowSimplifi } else if (allowSimplification && LiteralExpr.IsTrue(b)) { return b; } else { - var or = new BinaryExpr(a.tok, BinaryExpr.Opcode.Or, a, b); + var or = new BinaryExpr(a.Origin, BinaryExpr.Opcode.Or, a, b); or.ResolvedOp = BinaryExpr.ResolvedOpcode.Or; // resolve here or.Type = Type.Bool; // resolve here return or; @@ -695,7 +757,7 @@ public static Expression CreateITE(Expression test, Expression e0, Expression e1 Contract.Requires(e1 != null); Contract.Requires(test.Type.IsBoolType && e0.Type.Equals(e1.Type)); Contract.Ensures(Contract.Result() != null); - var ite = new ITEExpr(test.tok, false, test, e0, e1); + var ite = new ITEExpr(test.Origin, false, test, e0, e1); ite.Type = e0.type; // resolve here return ite; } @@ -705,12 +767,12 @@ public static Expression CreateITE(Expression test, Expression e0, Expression e1 /// enables resolving a syntactic clone of this resolved expression. /// Expects "receiver" and each of the "arguments" to be a resolved expression. /// - public static Expression CreateResolvedCall(IToken tok, Expression receiver, Function function, List arguments, + public static Expression CreateResolvedCall(IOrigin tok, Expression receiver, Function function, List arguments, List typeArguments, SystemModuleManager systemModuleManager) { - Contract.Requires(function.Formals.Count == arguments.Count); + Contract.Requires(function.Ins.Count == arguments.Count); Contract.Requires(function.TypeArgs.Count == typeArguments.Count); - var call = new FunctionCallExpr(tok, function.Name, receiver, tok, tok, arguments) { + var call = new FunctionCallExpr(tok, function.NameNode, receiver, tok, Token.NoToken, arguments) { Function = function, Type = function.ResultType, TypeApplication_AtEnclosingClass = receiver.Type.TypeArgs, @@ -728,12 +790,12 @@ public static Expression WrapResolvedCall(FunctionCallExpr call, SystemModuleMan var receiverType = (UserDefinedType)call.Receiver.Type.NormalizeExpand(); var subst = TypeParameter.SubstitutionMap(receiverType.ResolvedClass.TypeArgs, receiverType.TypeArgs); subst = ModuleResolver.AddParentTypeParameterSubstitutions(subst, receiverType); - var exprDotName = new ExprDotName(call.tok, call.Receiver, call.Function.Name, call.TypeApplication_JustFunction) { + var exprDotName = new ExprDotName(call.Origin, call.Receiver, call.Function.NameNode, call.TypeApplication_JustFunction) { Type = ModuleResolver.SelectAppropriateArrowTypeForFunction(call.Function, subst, systemModuleManager) }; subst = TypeParameter.SubstitutionMap(call.Function.TypeArgs, call.TypeApplication_JustFunction); - return new ApplySuffix(call.tok, null, exprDotName, new ActualBindings(call.Args).ArgumentBindings, call.tok) { + return new ApplySuffix(call.Origin, null, exprDotName, new ActualBindings(call.Args).ArgumentBindings, call.CloseParen) { ResolvedExpression = call, Type = call.Function.ResultType.Subst(subst) }; @@ -743,7 +805,7 @@ public static Expression WrapResolvedCall(FunctionCallExpr call, SystemModuleMan /// Create a resolved field-selection expression. /// Expects "receiver" to be a resolved expression. /// - public static Expression CreateFieldSelect(IToken tok, Expression receiver, Field field) { + public static Expression CreateFieldSelect(IOrigin tok, Expression receiver, Field field) { var memberSelectExpr = new MemberSelectExpr(tok, receiver, field); return WrapResolvedMemberSelect(memberSelectExpr); } @@ -752,13 +814,25 @@ public static Expression CreateFieldSelect(IToken tok, Expression receiver, Fiel /// Wrap the resolved MemberSelectExpr in the usual unresolved structure, in case the expression is cloned and re-resolved. /// public static Expression WrapResolvedMemberSelect(MemberSelectExpr memberSelectExpr) { - List optTypeArguments = memberSelectExpr.TypeApplication_JustMember.Count == 0 ? null : memberSelectExpr.TypeApplication_JustMember; - return new ExprDotName(memberSelectExpr.tok, memberSelectExpr.Obj, memberSelectExpr.MemberName, optTypeArguments) { + List optTypeArguments = memberSelectExpr.TypeApplicationJustMember.Count == 0 ? null : memberSelectExpr.TypeApplicationJustMember; + return new ExprDotName(memberSelectExpr.Origin, memberSelectExpr.Obj, memberSelectExpr.MemberNameNode, optTypeArguments) { ResolvedExpression = memberSelectExpr, Type = memberSelectExpr.Type }; } + /// + /// If "expr" is an expression that exists only as a resolved expression, then wrap it in the usual unresolved structure. + /// + public static Expression WrapAsParsedStructureIfNecessary(Expression expr, SystemModuleManager systemModuleManager) { + if (expr is FunctionCallExpr functionCallExpr) { + return WrapResolvedCall(functionCallExpr, systemModuleManager); + } else if (expr is MemberSelectExpr memberSelectExpr) { + return WrapResolvedMemberSelect(memberSelectExpr); + } + return expr; + } + /// /// Create a resolved case expression for a match expression /// @@ -771,7 +845,7 @@ public static MatchCaseExpr CreateMatchCase(MatchCaseExpr old_case, Expression n var newVars = old_case.Arguments.ConvertAll(bv => cloner.CloneBoundVar(bv, false)); new_body = VarSubstituter(old_case.Arguments.ConvertAll(x => (NonglobalVariable)x), newVars, new_body); - var new_case = new MatchCaseExpr(old_case.tok, old_case.Ctor, old_case.FromBoundVar, newVars, new_body, old_case.Attributes); + var new_case = new MatchCaseExpr(old_case.Origin, old_case.Ctor, old_case.FromBoundVar, newVars, new_body, old_case.Attributes); new_case.Ctor = old_case.Ctor; // resolve here return new_case; @@ -780,7 +854,7 @@ public static MatchCaseExpr CreateMatchCase(MatchCaseExpr old_case, Expression n /// /// Create a match expression with a resolved type /// - public static Expression CreateMatch(IToken tok, Expression src, List cases, Type type) { + public static Expression CreateMatch(IOrigin tok, Expression src, List cases, Type type) { MatchExpr e = new MatchExpr(tok, src, cases, false); e.Type = type; // resolve here @@ -790,7 +864,7 @@ public static Expression CreateMatch(IToken tok, Expression src, List /// Create a let expression with a resolved type and fresh variables /// - public static Expression CreateLet(IToken tok, List> LHSs, List RHSs, Expression body, bool exact) { + public static Expression CreateLet(IOrigin tok, List> LHSs, List RHSs, Expression body, bool exact) { Contract.Requires(tok != null); Contract.Requires(LHSs != null && RHSs != null); Contract.Requires(LHSs.Count == RHSs.Count); @@ -828,9 +902,9 @@ public static Expression CreateQuantifier(QuantifierExpr expr, bool forall, Expr QuantifierExpr q; if (forall) { - q = new ForallExpr(expr.tok, expr.RangeToken, newVars, expr.Range, body, expr.Attributes); + q = new ForallExpr(expr.Origin, newVars, expr.Range, body, expr.Attributes); } else { - q = new ExistsExpr(expr.tok, expr.RangeToken, newVars, expr.Range, body, expr.Attributes); + q = new ExistsExpr(expr.Origin, newVars, expr.Range, body, expr.Attributes); } q.Type = Type.Bool; @@ -842,7 +916,7 @@ public static Expression CreateQuantifier(QuantifierExpr expr, bool forall, Expr /// public static Expression CreateIdentExpr(IVariable v) { Contract.Requires(v != null); - return new IdentifierExpr(v.Tok, v.Name) { + return new IdentifierExpr(v.Origin, v.Name) { Var = v, type = v.Type }; @@ -858,7 +932,7 @@ public static Expression VarSubstituter(List oldVars, List Children => SubExpressions; public override IEnumerable PreResolveChildren => Children; -} \ No newline at end of file + + public static Expression CreateAssigned(IOrigin tok, IdentifierExpr inner) { + return new UnaryOpExpr(tok, UnaryOpExpr.Opcode.Assigned, inner) { + Type = Type.Bool + }; + } +} diff --git a/Source/DafnyCore/AST/Expressions/Heap/FrameExpression.cs b/Source/DafnyCore/AST/Expressions/Heap/FrameExpression.cs index fa767652ceb..0082de8dd91 100644 --- a/Source/DafnyCore/AST/Expressions/Heap/FrameExpression.cs +++ b/Source/DafnyCore/AST/Expressions/Heap/FrameExpression.cs @@ -4,7 +4,7 @@ namespace Microsoft.Dafny; -public class FrameExpression : TokenNode, IHasUsages { +public class FrameExpression : NodeWithComputedRange, IHasReferences { public readonly Expression OriginalExpression; // may be a WildcardExpr [FilledInDuringResolution] public Expression DesugaredExpression; // may be null for modifies clauses, even after resolution @@ -26,17 +26,15 @@ void ObjectInvariant() { /// If a "fieldName" is given, then "tok" denotes its source location. Otherwise, "tok" /// denotes the source location of "e". /// - public FrameExpression(IToken tok, Expression e, string fieldName) { - Contract.Requires(tok != null); + public FrameExpression(IOrigin origin, Expression e, string fieldName) : base(origin) { + Contract.Requires(origin != null); Contract.Requires(e != null); Contract.Requires(!(e is WildcardExpr) || fieldName == null); - this.tok = tok; OriginalExpression = e; FieldName = fieldName; } - public FrameExpression(Cloner cloner, FrameExpression original) { - this.tok = cloner.Tok(original.tok); + public FrameExpression(Cloner cloner, FrameExpression original) : base(cloner, original) { OriginalExpression = cloner.CloneExpr(original.OriginalExpression); FieldName = original.FieldName; @@ -48,10 +46,9 @@ public FrameExpression(Cloner cloner, FrameExpression original) { } } - public IToken NameToken => tok; public override IEnumerable Children => new[] { E }; public override IEnumerable PreResolveChildren => Children; - public IEnumerable GetResolvedDeclarations() { - return new[] { Field }.Where(x => x != null); + public IEnumerable GetReferences() { + return Field == null ? Enumerable.Empty() : new[] { new Reference(Origin, Field) }; } } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/Heap/FreshExpr.cs b/Source/DafnyCore/AST/Expressions/Heap/FreshExpr.cs index d3385c9f2e4..de0e4177af7 100644 --- a/Source/DafnyCore/AST/Expressions/Heap/FreshExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Heap/FreshExpr.cs @@ -6,9 +6,9 @@ public class FreshExpr : UnaryOpExpr, ICloneable { public readonly string/*?*/ At; [FilledInDuringResolution] public Label/*?*/ AtLabel; // after that, At==null iff AtLabel==null - public FreshExpr(IToken tok, Expression e, string at = null) - : base(tok, Opcode.Fresh, e) { - Contract.Requires(tok != null); + public FreshExpr(IOrigin origin, Expression e, string at = null) + : base(origin, Opcode.Fresh, e) { + Contract.Requires(origin != null); Contract.Requires(e != null); this.At = at; } diff --git a/Source/DafnyCore/AST/Expressions/Heap/OldExpr.cs b/Source/DafnyCore/AST/Expressions/Heap/OldExpr.cs index 3c0c51c84e5..e523d3b7014 100644 --- a/Source/DafnyCore/AST/Expressions/Heap/OldExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Heap/OldExpr.cs @@ -28,9 +28,9 @@ public OldExpr Clone(Cloner cloner) { } [Captured] - public OldExpr(IToken tok, Expression expr, string at = null) - : base(tok) { - Contract.Requires(tok != null); + public OldExpr(IOrigin origin, Expression expr, string at = null) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(expr != null); cce.Owner.AssignSame(this, expr); E = expr; diff --git a/Source/DafnyCore/AST/Expressions/Heap/UnchangedExpr.cs b/Source/DafnyCore/AST/Expressions/Heap/UnchangedExpr.cs index aa30c2828bd..4f01b0286a9 100644 --- a/Source/DafnyCore/AST/Expressions/Heap/UnchangedExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Heap/UnchangedExpr.cs @@ -24,9 +24,9 @@ public UnchangedExpr(Cloner cloner, UnchangedExpr original) : base(cloner, origi } } - public UnchangedExpr(IToken tok, List frame, string/*?*/ at) - : base(tok) { - Contract.Requires(tok != null); + public UnchangedExpr(IOrigin origin, List frame, string/*?*/ at) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(frame != null); this.Frame = frame; this.At = at; diff --git a/Source/DafnyCore/AST/Expressions/Heap/WildcardExpr.cs b/Source/DafnyCore/AST/Expressions/Heap/WildcardExpr.cs index c39d47f0817..831ef5bc0ad 100644 --- a/Source/DafnyCore/AST/Expressions/Heap/WildcardExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Heap/WildcardExpr.cs @@ -7,9 +7,9 @@ public class WildcardExpr : Expression, ICloneable { // a Wildcar public WildcardExpr(Cloner cloner, WildcardExpr original) : base(cloner, original) { } - public WildcardExpr(IToken tok) - : base(tok) { - Contract.Requires(tok != null); + public WildcardExpr(IOrigin origin) + : base(origin) { + Contract.Requires(origin != null); } public WildcardExpr Clone(Cloner cloner) { diff --git a/Source/DafnyCore/AST/Expressions/LiteralExpr.cs b/Source/DafnyCore/AST/Expressions/LiteralExpr.cs index 9d5cc860c0c..897f3314e55 100644 --- a/Source/DafnyCore/AST/Expressions/LiteralExpr.cs +++ b/Source/DafnyCore/AST/Expressions/LiteralExpr.cs @@ -48,36 +48,36 @@ public static bool IsEmptySequence(Expression e) { return StripParens(e) is SeqDisplayExpr display && display.Elements.Count == 0; } - public LiteralExpr(IToken tok) - : base(tok) { // represents the Dafny literal "null" - Contract.Requires(tok != null); + public LiteralExpr(IOrigin origin) + : base(origin) { // represents the Dafny literal "null" + Contract.Requires(origin != null); this.Value = null; } - public LiteralExpr(IToken tok, BigInteger n) - : base(tok) { - Contract.Requires(tok != null); + public LiteralExpr(IOrigin origin, BigInteger n) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(0 <= n.Sign); this.Value = n; } - public LiteralExpr(IToken tok, BaseTypes.BigDec n) - : base(tok) { + public LiteralExpr(IOrigin origin, BaseTypes.BigDec n) + : base(origin) { Contract.Requires(0 <= n.Mantissa.Sign); - Contract.Requires(tok != null); + Contract.Requires(origin != null); this.Value = n; } - public LiteralExpr(IToken tok, int n) - : base(tok) { - Contract.Requires(tok != null); + public LiteralExpr(IOrigin origin, int n) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(0 <= n); this.Value = new BigInteger(n); } - public LiteralExpr(IToken tok, bool b) - : base(tok) { - Contract.Requires(tok != null); + public LiteralExpr(IOrigin origin, bool b) + : base(origin) { + Contract.Requires(origin != null); this.Value = b; } @@ -86,9 +86,9 @@ public LiteralExpr(IToken tok, bool b) /// two reasons: both of these literals store a string in .Value, and string literals also carry an /// additional field. /// - protected LiteralExpr(IToken tok, string s) - : base(tok) { - Contract.Requires(tok != null); + protected LiteralExpr(IOrigin origin, string s) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(s != null); this.Value = s; } @@ -103,8 +103,8 @@ public LiteralExpr Clone(Cloner cloner) { } public class CharLiteralExpr : LiteralExpr, ICloneable { - public CharLiteralExpr(IToken tok, string s) - : base(tok, s) { + public CharLiteralExpr(IOrigin origin, string s) + : base(origin, s) { Contract.Requires(s != null); } @@ -118,8 +118,8 @@ public CharLiteralExpr(Cloner cloner, CharLiteralExpr original) : base(cloner, o public class StringLiteralExpr : LiteralExpr, ICloneable { public readonly bool IsVerbatim; - public StringLiteralExpr(IToken tok, string s, bool isVerbatim) - : base(tok, s) { + public StringLiteralExpr(IOrigin origin, string s, bool isVerbatim) + : base(origin, s) { Contract.Requires(s != null); IsVerbatim = isVerbatim; } @@ -148,9 +148,9 @@ public NegationExpression(Cloner cloner, NegationExpression original) : base(clo E = cloner.CloneExpr(original.E); } - public NegationExpression(IToken tok, Expression e) - : base(tok) { - Contract.Requires(tok != null); + public NegationExpression(IOrigin origin, Expression e) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(e != null); E = e; } diff --git a/Source/DafnyCore/AST/Expressions/Operators/BinaryExpr.cs b/Source/DafnyCore/AST/Expressions/Operators/BinaryExpr.cs index 5eec0fe5eda..08794f0f865 100644 --- a/Source/DafnyCore/AST/Expressions/Operators/BinaryExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Operators/BinaryExpr.cs @@ -51,8 +51,8 @@ public enum ResolvedOpcode { Le, Ge, Gt, - Add, - Sub, + Add, // also used for char + Sub, // also used for char Mul, Div, Mod, @@ -114,14 +114,14 @@ public enum ResolvedOpcode { } private ResolvedOpcode _theResolvedOp = ResolvedOpcode.YetUndetermined; public ResolvedOpcode ResolvedOp { - set { - Contract.Assume(_theResolvedOp == ResolvedOpcode.YetUndetermined || _theResolvedOp == value); // there's never a reason for resolution to change its mind, is there? - _theResolvedOp = value; - } get { Debug.Assert(_theResolvedOp != ResolvedOpcode.YetUndetermined); // shouldn't read it until it has been properly initialized return _theResolvedOp; } + set { + Contract.Assume(_theResolvedOp == ResolvedOpcode.YetUndetermined || _theResolvedOp == value); // there's never a reason for resolution to change its mind, is there? + _theResolvedOp = value; + } } public ResolvedOpcode ResolvedOp_PossiblyStillUndetermined { // offer a way to return _theResolveOp -- for experts only! get { return _theResolvedOp; } @@ -325,10 +325,10 @@ public BinaryExpr(Cloner cloner, BinaryExpr original) : base(cloner, original) { } } - public BinaryExpr(IToken tok, Opcode op, Expression e0, Expression e1) + public BinaryExpr(IOrigin origin, Opcode op, Expression e0, Expression e1) : - base(tok) { - Contract.Requires(tok != null); + base(origin) { + Contract.Requires(origin != null); Contract.Requires(e0 != null); Contract.Requires(e1 != null); this.Op = op; @@ -339,8 +339,8 @@ public BinaryExpr(IToken tok, Opcode op, Expression e0, Expression e1) /// /// Returns a resolved binary expression /// - public BinaryExpr(IToken tok, BinaryExpr.ResolvedOpcode rop, Expression e0, Expression e1) - : this(tok, BinaryExpr.ResolvedOp2SyntacticOp(rop), e0, e1) { + public BinaryExpr(IOrigin origin, BinaryExpr.ResolvedOpcode rop, Expression e0, Expression e1) + : this(origin, BinaryExpr.ResolvedOp2SyntacticOp(rop), e0, e1) { ResolvedOp = rop; switch (rop) { case ResolvedOpcode.EqCommon: diff --git a/Source/DafnyCore/AST/Expressions/Operators/ChainingExpression.cs b/Source/DafnyCore/AST/Expressions/Operators/ChainingExpression.cs index 94092be9558..2cf4d3e765e 100644 --- a/Source/DafnyCore/AST/Expressions/Operators/ChainingExpression.cs +++ b/Source/DafnyCore/AST/Expressions/Operators/ChainingExpression.cs @@ -8,7 +8,7 @@ namespace Microsoft.Dafny; public class ChainingExpression : ConcreteSyntaxExpression, ICloneable, ICanFormat { public readonly List Operands; public readonly List Operators; - public readonly List OperatorLocs; + public readonly List OperatorLocs; public readonly List PrefixLimits; public readonly Expression E; @@ -19,14 +19,14 @@ public ChainingExpression Clone(Cloner cloner) { public ChainingExpression(Cloner cloner, ChainingExpression original) : base(cloner, original) { Operands = original.Operands.Select(cloner.CloneExpr).ToList(); Operators = original.Operators; - OperatorLocs = original.OperatorLocs.Select(cloner.Tok).ToList(); + OperatorLocs = original.OperatorLocs.Select(cloner.Origin).ToList(); PrefixLimits = original.PrefixLimits.Select(cloner.CloneExpr).ToList(); E = ComputeDesugaring(Operands, Operators, OperatorLocs, PrefixLimits); } - public ChainingExpression(IToken tok, List operands, List operators, List operatorLocs, List prefixLimits) - : base(tok) { - Contract.Requires(tok != null); + public ChainingExpression(IOrigin origin, List operands, List operators, List operatorLocs, List prefixLimits) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(operands != null); Contract.Requires(operators != null); Contract.Requires(operatorLocs != null); @@ -44,7 +44,7 @@ public ChainingExpression(IToken tok, List operands, List operands, List operators, List operatorLocs, List prefixLimits) { + private static Expression ComputeDesugaring(List operands, List operators, List operatorLocs, List prefixLimits) { Expression desugaring; // Compute the desugaring if (operators[0] == BinaryExpr.Opcode.Disjoint) { diff --git a/Source/DafnyCore/AST/Expressions/Operators/DecreasesToExpr.cs b/Source/DafnyCore/AST/Expressions/Operators/DecreasesToExpr.cs new file mode 100644 index 00000000000..441beb6db09 --- /dev/null +++ b/Source/DafnyCore/AST/Expressions/Operators/DecreasesToExpr.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Dafny; + +public class DecreasesToExpr : Expression, ICloneable { + public IReadOnlyList OldExpressions { get; } + public IReadOnlyList NewExpressions { get; } + + public bool AllowNoChange { get; } + + public DecreasesToExpr(IOrigin origin, + IReadOnlyList oldExpressions, + IReadOnlyList newExpressions, bool allowNoChange) : base(origin) { + OldExpressions = oldExpressions; + NewExpressions = newExpressions; + AllowNoChange = allowNoChange; + } + + public DecreasesToExpr(Cloner cloner, DecreasesToExpr original) : base(cloner, original) { + OldExpressions = original.OldExpressions.Select(cloner.CloneExpr).ToList(); + NewExpressions = original.NewExpressions.Select(cloner.CloneExpr).ToList(); + AllowNoChange = original.AllowNoChange; + } + + public DecreasesToExpr Clone(Cloner cloner) { + return new DecreasesToExpr(cloner, this); + } + + public override IEnumerable SubExpressions => OldExpressions.Concat(NewExpressions); +} diff --git a/Source/DafnyCore/AST/Expressions/Operators/ParensExpression.cs b/Source/DafnyCore/AST/Expressions/Operators/ParensExpression.cs index 809b03ffadd..bac7e4a870c 100644 --- a/Source/DafnyCore/AST/Expressions/Operators/ParensExpression.cs +++ b/Source/DafnyCore/AST/Expressions/Operators/ParensExpression.cs @@ -4,8 +4,8 @@ namespace Microsoft.Dafny; public class ParensExpression : ConcreteSyntaxExpression, ICanFormat, ICloneable { public readonly Expression E; - public ParensExpression(IToken tok, Expression e) - : base(tok) { + public ParensExpression(IOrigin origin, Expression e) + : base(origin) { E = e; } diff --git a/Source/DafnyCore/AST/Expressions/Operators/UnaryExpr.cs b/Source/DafnyCore/AST/Expressions/Operators/UnaryExpr.cs index bcc7cf05a61..d4f96327a2f 100644 --- a/Source/DafnyCore/AST/Expressions/Operators/UnaryExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Operators/UnaryExpr.cs @@ -10,9 +10,9 @@ void ObjectInvariant() { Contract.Invariant(E != null); } - public UnaryExpr(IToken tok, Expression e) - : base(tok) { - Contract.Requires(tok != null); + public UnaryExpr(IOrigin origin, Expression e) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(e != null); this.E = e; } diff --git a/Source/DafnyCore/AST/Expressions/Operators/UnaryOpExpr.cs b/Source/DafnyCore/AST/Expressions/Operators/UnaryOpExpr.cs index a81c0c6b84a..94ce2b5889e 100644 --- a/Source/DafnyCore/AST/Expressions/Operators/UnaryOpExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Operators/UnaryOpExpr.cs @@ -9,6 +9,7 @@ public enum Opcode { Fresh, // fresh also has a(n optional) second argument, namely the @-label Allocated, Lit, // there is no syntax for this operator, but it is sometimes introduced during translation + Assigned, } public readonly Opcode Op; @@ -22,7 +23,8 @@ public enum ResolvedOpcode { MapCard, Fresh, Allocated, - Lit + Lit, + Assigned, } private ResolvedOpcode _ResolvedOp = ResolvedOpcode.YetUndetermined; @@ -32,7 +34,7 @@ public ResolvedOpcode ResolveOp() { if (_ResolvedOp == ResolvedOpcode.YetUndetermined) { Contract.Assert(Type != null); Contract.Assert(Type is not TypeProxy); - _ResolvedOp = (Op, E.Type.NormalizeExpand()) switch { + _ResolvedOp = (Op, E.Type.NormalizeToAncestorType()) switch { (Opcode.Not, BoolType _) => ResolvedOpcode.BoolNot, (Opcode.Not, BitvectorType _) => ResolvedOpcode.BVNot, (Opcode.Cardinality, SeqType _) => ResolvedOpcode.SeqLength, @@ -42,6 +44,7 @@ public ResolvedOpcode ResolveOp() { (Opcode.Fresh, _) => ResolvedOpcode.Fresh, (Opcode.Allocated, _) => ResolvedOpcode.Allocated, (Opcode.Lit, _) => ResolvedOpcode.Lit, + (Opcode.Assigned, _) => ResolvedOpcode.Assigned, _ => ResolvedOpcode.YetUndetermined // Unreachable }; Contract.Assert(_ResolvedOp != ResolvedOpcode.YetUndetermined); @@ -56,9 +59,9 @@ public void SetResolveOp(ResolvedOpcode resolvedOpcode) { _ResolvedOp = resolvedOpcode; } - public UnaryOpExpr(IToken tok, Opcode op, Expression e) - : base(tok, e) { - Contract.Requires(tok != null); + public UnaryOpExpr(IOrigin origin, Opcode op, Expression e) + : base(origin, e) { + Contract.Requires(origin != null); Contract.Requires(e != null); Contract.Requires(op != Opcode.Fresh || this is FreshExpr); this.Op = op; diff --git a/Source/DafnyCore/AST/Expressions/Specification.cs b/Source/DafnyCore/AST/Expressions/Specification.cs index 58d10ffd234..e9529f2a7e2 100644 --- a/Source/DafnyCore/AST/Expressions/Specification.cs +++ b/Source/DafnyCore/AST/Expressions/Specification.cs @@ -3,7 +3,7 @@ namespace Microsoft.Dafny; -public class Specification : TokenNode, IAttributeBearingDeclaration +public class Specification : NodeWithComputedRange, IAttributeBearingDeclaration where T : Node { public readonly List Expressions; @@ -12,6 +12,11 @@ private void ObjectInvariant() { Contract.Invariant(Expressions == null || cce.NonNullElements(Expressions)); } + public Specification() { + Expressions = new List(); + Attributes = null; + } + public Specification(List exprs, Attributes attrs) { Contract.Requires(exprs == null || cce.NonNullElements(exprs)); Expressions = exprs; @@ -19,6 +24,7 @@ public Specification(List exprs, Attributes attrs) { } public Attributes Attributes { get; set; } + string IAttributeBearingDeclaration.WhatKind => "specification clause"; public bool HasAttributes() { return Attributes != null; diff --git a/Source/DafnyCore/AST/Expressions/StmtExpr.cs b/Source/DafnyCore/AST/Expressions/StmtExpr.cs index 0d34e99a999..4dc7be9946f 100644 --- a/Source/DafnyCore/AST/Expressions/StmtExpr.cs +++ b/Source/DafnyCore/AST/Expressions/StmtExpr.cs @@ -19,14 +19,14 @@ void ObjectInvariant() { public StmtExpr(Cloner cloner, StmtExpr original) : base(cloner, original) { E = cloner.CloneExpr(original.E); - S = cloner.CloneStmt(original.S); + S = cloner.CloneStmt(original.S, false); } public override IEnumerable Children => new Node[] { S, E }; - public StmtExpr(IToken tok, Statement stmt, Expression expr) - : base(tok) { - Contract.Requires(tok != null); + public StmtExpr(IOrigin origin, Statement stmt, Expression expr) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(stmt != null); Contract.Requires(expr != null); S = stmt; @@ -53,20 +53,25 @@ public override IEnumerable TerminalExpressions { /// S is executed. /// This method should be called only after successful resolution of the expression. /// - public Expression GetSConclusion() { - // this is one place where we actually investigate what kind of statement .S is - if (S is PredicateStmt) { - var s = (PredicateStmt)S; - return s.Expr; - } else if (S is CalcStmt) { - var s = (CalcStmt)S; - return s.Result; - } else if (S is RevealStmt) { - return new LiteralExpr(tok, true); // one could use the definition axiom or the referenced labeled assertions, but "true" is conservative and much simpler :) - } else if (S is UpdateStmt) { - return new LiteralExpr(tok, true); // one could use the postcondition of the method, suitably instantiated, but "true" is conservative and much simpler :) - } else { - Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement + public Expression GetStatementConclusion() { + return GetStatementConclusion(S); + } + + private Expression GetStatementConclusion(Statement statement) { + switch (statement) { + // this is one place where we actually investigate what kind of statement .S is + case PredicateStmt stmt: + return stmt.Expr; + case CalcStmt stmt: + return stmt.Result; + case HideRevealStmt: + return CreateBoolLiteral(Origin, true); // one could use the definition axiom or the referenced labeled assertions, but "true" is conservative and much simpler :) + case AssignStatement: + return CreateBoolLiteral(Origin, true); // one could use the postcondition of the method, suitably instantiated, but "true" is conservative and much simpler :) + case BlockByProofStmt stmt: + return GetStatementConclusion(stmt.Body); + default: + Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } } diff --git a/Source/DafnyCore/AST/Expressions/Types/BoxingCastExpr.cs b/Source/DafnyCore/AST/Expressions/Types/BoxingCastExpr.cs index 39415c325b2..149657e2acd 100644 --- a/Source/DafnyCore/AST/Expressions/Types/BoxingCastExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Types/BoxingCastExpr.cs @@ -15,7 +15,7 @@ void ObjectInvariant() { } public BoxingCastExpr(Expression e, Type fromType, Type toType) - : base(e.tok) { + : base(e.Origin) { Contract.Requires(e != null); Contract.Requires(fromType != null); Contract.Requires(toType != null); diff --git a/Source/DafnyCore/AST/Expressions/Types/ConversionExpr.cs b/Source/DafnyCore/AST/Expressions/Types/ConversionExpr.cs index 710b98fe58c..9c0cb879651 100644 --- a/Source/DafnyCore/AST/Expressions/Types/ConversionExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Types/ConversionExpr.cs @@ -9,9 +9,9 @@ public ConversionExpr(Cloner cloner, ConversionExpr original) : base(cloner, ori messagePrefix = original.messagePrefix; } - public ConversionExpr(IToken tok, Expression expr, Type toType, string messagePrefix = "") - : base(tok, expr, toType) { - Contract.Requires(tok != null); + public ConversionExpr(IOrigin origin, Expression expr, Type toType, string messagePrefix = "") + : base(origin, expr, toType) { + Contract.Requires(origin != null); Contract.Requires(expr != null); Contract.Requires(toType != null); this.messagePrefix = messagePrefix; diff --git a/Source/DafnyCore/AST/Expressions/Types/TypeTestExpr.cs b/Source/DafnyCore/AST/Expressions/Types/TypeTestExpr.cs index e0d717795b7..1bad77e1212 100644 --- a/Source/DafnyCore/AST/Expressions/Types/TypeTestExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Types/TypeTestExpr.cs @@ -6,9 +6,9 @@ public class TypeTestExpr : TypeUnaryExpr, ICloneable { public TypeTestExpr(Cloner cloner, TypeTestExpr original) : base(cloner, original) { } - public TypeTestExpr(IToken tok, Expression expr, Type toType) - : base(tok, expr, toType) { - Contract.Requires(tok != null); + public TypeTestExpr(IOrigin origin, Expression expr, Type toType) + : base(origin, expr, toType) { + Contract.Requires(origin != null); Contract.Requires(expr != null); Contract.Requires(toType != null); } diff --git a/Source/DafnyCore/AST/Expressions/Types/TypeUnaryExpr.cs b/Source/DafnyCore/AST/Expressions/Types/TypeUnaryExpr.cs index e270e5d04c8..073ba582784 100644 --- a/Source/DafnyCore/AST/Expressions/Types/TypeUnaryExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Types/TypeUnaryExpr.cs @@ -11,9 +11,9 @@ protected TypeUnaryExpr(Cloner cloner, TypeUnaryExpr original) : base(cloner, or ToType = cloner.CloneType(original.ToType); } - protected TypeUnaryExpr(IToken tok, Expression expr, Type toType) - : base(tok, expr) { - Contract.Requires(tok != null); + protected TypeUnaryExpr(IOrigin origin, Expression expr, Type toType) + : base(origin, expr) { + Contract.Requires(origin != null); Contract.Requires(expr != null); Contract.Requires(toType != null); ToType = toType; diff --git a/Source/DafnyCore/AST/Expressions/Types/UnboxingCastExpr.cs b/Source/DafnyCore/AST/Expressions/Types/UnboxingCastExpr.cs index fd8692cc8bf..8aa28019020 100644 --- a/Source/DafnyCore/AST/Expressions/Types/UnboxingCastExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Types/UnboxingCastExpr.cs @@ -15,7 +15,7 @@ void ObjectInvariant() { } public UnboxingCastExpr(Expression e, Type fromType, Type toType) - : base(e.tok) { + : base(e.Origin) { Contract.Requires(e != null); Contract.Requires(fromType != null); Contract.Requires(toType != null); diff --git a/Source/DafnyCore/AST/Expressions/Variables/AutoGhostIdentifierExpr.cs b/Source/DafnyCore/AST/Expressions/Variables/AutoGhostIdentifierExpr.cs index a4c64255745..d244205ccef 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/AutoGhostIdentifierExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/AutoGhostIdentifierExpr.cs @@ -7,8 +7,8 @@ namespace Microsoft.Dafny; /// parser and parts of the resolver. /// public class AutoGhostIdentifierExpr : IdentifierExpr, ICloneable { - public AutoGhostIdentifierExpr(IToken tok, string name) - : base(new AutoGeneratedToken(tok), name) { } + public AutoGhostIdentifierExpr(IOrigin tok, string name) + : base(new AutoGeneratedOrigin(tok), name) { } public AutoGhostIdentifierExpr(Cloner cloner, AutoGhostIdentifierExpr original) : base(cloner, original) { diff --git a/Source/DafnyCore/AST/Expressions/Variables/BoundVar.cs b/Source/DafnyCore/AST/Expressions/Variables/BoundVar.cs index db914c9c9f6..c9ec6c273e9 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/BoundVar.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/BoundVar.cs @@ -1,15 +1,19 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; +using JetBrains.Annotations; namespace Microsoft.Dafny; -[DebuggerDisplay("Bound<{name}>")] public class BoundVar : NonglobalVariable { public override bool IsMutable => false; - public BoundVar(IToken tok, string name, Type type) - : base(tok, name, type, false) { - Contract.Requires(tok != null); + + public BoundVar(string name, Type type) : this(Token.NoToken, new Name(Token.NoToken, name), type) { } + public BoundVar(IOrigin origin, string name, Type type) : this(origin, new Name(origin.StartToken, name), type) { } + + public BoundVar(IOrigin origin, Name name, Type type) + : base(origin, name, type, false) { + Contract.Requires(origin != null); Contract.Requires(name != null); Contract.Requires(type != null); } @@ -26,7 +30,7 @@ public class QuantifiedVar : BoundVar { public readonly Expression Domain; public readonly Expression Range; - public QuantifiedVar(IToken tok, string name, Type type, Expression domain, Expression range) + public QuantifiedVar(IOrigin tok, string name, Type type, Expression domain, Expression range) : base(tok, name, type) { Contract.Requires(tok != null); Contract.Requires(name != null); @@ -36,7 +40,7 @@ public QuantifiedVar(IToken tok, string name, Type type, Expression domain, Expr } /// - /// Map a list of quantified variables to an eqivalent list of bound variables plus a single range expression. + /// Map a list of quantified variables to an equivalent list of bound variables plus a single range expression. /// The transformation looks like this in general: /// /// x1 <- C1 | E1, ..., xN <- CN | EN @@ -48,25 +52,25 @@ public QuantifiedVar(IToken tok, string name, Type type, Expression domain, Expr /// Note the result will be null rather than "true" if there are no such domains or ranges. /// Some quantification contexts (such as comprehensions) will replace this with "true". /// - public static void ExtractSingleRange(List qvars, out List bvars, out Expression range) { + public static void ExtractSingleRange(List qvars, out List bvars, [CanBeNull] out Expression range) { bvars = new List(); range = null; foreach (var qvar in qvars) { - BoundVar bvar = new BoundVar(qvar.tok, qvar.Name, qvar.SyntacticType); + BoundVar bvar = new BoundVar(qvar.Origin, qvar.Name, qvar.SyntacticType); bvars.Add(bvar); if (qvar.Domain != null) { // Attach a token wrapper so we can produce a better error message if the domain is not a collection var domainWithToken = QuantifiedVariableDomainCloner.Instance.CloneExpr(qvar.Domain); - var inDomainExpr = new BinaryExpr(domainWithToken.tok, BinaryExpr.Opcode.In, new IdentifierExpr(bvar.tok, bvar), domainWithToken); - range = range == null ? inDomainExpr : new BinaryExpr(domainWithToken.tok, BinaryExpr.Opcode.And, range, inDomainExpr); + var inDomainExpr = new BinaryExpr(domainWithToken.Origin.Center, BinaryExpr.Opcode.In, new IdentifierExpr(bvar.Origin, bvar), domainWithToken); + range = range == null ? inDomainExpr : new BinaryExpr(domainWithToken.Origin.Center, BinaryExpr.Opcode.And, range, inDomainExpr); } if (qvar.Range != null) { // Attach a token wrapper so we can produce a better error message if the range is not a boolean expression var rangeWithToken = QuantifiedVariableRangeCloner.Instance.CloneExpr(qvar.Range); - range = range == null ? qvar.Range : new BinaryExpr(rangeWithToken.tok, BinaryExpr.Opcode.And, range, rangeWithToken); + range = range == null ? qvar.Range : new BinaryExpr(rangeWithToken.Origin.Center, BinaryExpr.Opcode.And, range, rangeWithToken); } } } @@ -84,15 +88,23 @@ public IEnumerable AllBoundVars { class QuantifiedVariableDomainCloner : Cloner { public static readonly QuantifiedVariableDomainCloner Instance = new QuantifiedVariableDomainCloner(); private QuantifiedVariableDomainCloner() { } - public override IToken Tok(IToken tok) { - return new QuantifiedVariableDomainToken(tok); + public override IOrigin Origin(IOrigin tok) { + if (tok == null) { + return null; + } + + return new QuantifiedVariableDomainOrigin(tok); } } class QuantifiedVariableRangeCloner : Cloner { public static readonly QuantifiedVariableRangeCloner Instance = new QuantifiedVariableRangeCloner(); private QuantifiedVariableRangeCloner() { } - public override IToken Tok(IToken tok) { - return new QuantifiedVariableRangeToken(tok); + public override IOrigin Origin(IOrigin tok) { + if (tok == null) { + return null; + } + + return new QuantifiedVariableRangeOrigin(tok); } } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/Variables/CasePattern.cs b/Source/DafnyCore/AST/Expressions/Variables/CasePattern.cs index 7dca93bd5e1..2e89f8e0b93 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/CasePattern.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/CasePattern.cs @@ -13,7 +13,7 @@ namespace Microsoft.Dafny; /// which it is; in this case, Var is non-null, because this is the only place where Var.IsGhost /// is recorded by the parser. /// -public class CasePattern : TokenNode +public class CasePattern : NodeWithComputedRange where VT : class, IVariable { public readonly string Id; // After successful resolution, exactly one of the following two fields is non-null. @@ -29,8 +29,7 @@ public void MakeAConstructor() { this.Arguments = new List>(); } - public CasePattern(Cloner cloner, CasePattern original) { - tok = cloner.Tok(original.tok); + public CasePattern(Cloner cloner, CasePattern original) : base(cloner, original) { Id = original.Id; if (original.Var != null) { Var = cloner.CloneIVariable(original.Var, false); @@ -51,18 +50,16 @@ public CasePattern(Cloner cloner, CasePattern original) { } } - public CasePattern(IToken tok, string id, [Captured] List> arguments) { - Contract.Requires(tok != null); + public CasePattern(IOrigin origin, string id, [Captured] List> arguments) : base(origin) { + Contract.Requires(origin != null); Contract.Requires(id != null); - this.tok = tok; Id = id; Arguments = arguments; } - public CasePattern(IToken tok, VT bv) { - Contract.Requires(tok != null); + public CasePattern(IOrigin origin, VT bv) : base(origin) { + Contract.Requires(origin != null); Contract.Requires(bv != null); - this.tok = tok; Id = bv.Name; Var = bv; } @@ -75,13 +72,13 @@ public void AssembleExpr(List dtvTypeArgs) { Contract.Requires(Var != null || dtvTypeArgs != null); if (Var != null) { Contract.Assert(this.Id == this.Var.Name); - this.Expr = new IdentifierExpr(this.tok, this.Var); + this.Expr = new IdentifierExpr(this.Origin, this.Var); } else { - var dtValue = new DatatypeValue(this.tok, this.Ctor.EnclosingDatatype.Name, this.Id, + var dtValue = new DatatypeValue(this.Origin, this.Ctor.EnclosingDatatype.Name, this.Id, this.Arguments == null ? new List() : this.Arguments.ConvertAll(arg => arg.Expr)); dtValue.Ctor = this.Ctor; // resolve here dtValue.InferredTypeArgs.AddRange(dtvTypeArgs); // resolve here - dtValue.Type = new UserDefinedType(this.tok, this.Ctor.EnclosingDatatype.Name, this.Ctor.EnclosingDatatype, dtvTypeArgs); + dtValue.Type = new UserDefinedType(this.Origin, this.Ctor.EnclosingDatatype.Name, this.Ctor.EnclosingDatatype, dtvTypeArgs); this.Expr = dtValue; } } @@ -94,11 +91,11 @@ public void AssembleExprPreType(List dtvPreTypeArgs) { Contract.Requires(Var != null || dtvPreTypeArgs != null); if (Var != null) { Contract.Assert(this.Id == this.Var.Name); - this.Expr = new IdentifierExpr(this.tok, this.Var) { + this.Expr = new IdentifierExpr(this.Origin, this.Var) { PreType = this.Var.PreType }; } else { - var dtValue = new DatatypeValue(this.tok, this.Ctor.EnclosingDatatype.Name, this.Id, + var dtValue = new DatatypeValue(this.Origin, this.Ctor.EnclosingDatatype.Name, this.Id, this.Arguments == null ? new List() : this.Arguments.ConvertAll(arg => arg.Expr)) { Ctor = this.Ctor, PreType = new DPreType(this.Ctor.EnclosingDatatype, dtvPreTypeArgs) diff --git a/Source/DafnyCore/AST/Expressions/Variables/Formal.cs b/Source/DafnyCore/AST/Expressions/Variables/Formal.cs index f5716d290cb..c123f797d9e 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/Formal.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/Formal.cs @@ -5,41 +5,48 @@ namespace Microsoft.Dafny; public class Formal : NonglobalVariable { + public Attributes Attributes { get; set; } + public readonly bool InParam; // true to in-parameter, false for out-parameter public override bool IsMutable => !InParam; public readonly bool IsOld; - public readonly Expression DefaultValue; + public Expression DefaultValue; public readonly bool IsNameOnly; public readonly bool IsOlder; public readonly string NameForCompilation; - public Formal(IToken tok, string name, Type type, bool inParam, bool isGhost, Expression defaultValue, + public Formal(IOrigin origin, string name, Type type, bool inParam, bool isGhost, Expression defaultValue, + Attributes attributes = null, bool isOld = false, bool isNameOnly = false, bool isOlder = false, string nameForCompilation = null) - : base(tok, name, type, isGhost) { - Contract.Requires(tok != null); - Contract.Requires(name != null); + : this(origin, new Name(origin.StartToken, name), type, inParam, isGhost, defaultValue, attributes, + isOld, isNameOnly, isOlder, nameForCompilation) { + } + + public Formal(IOrigin origin, Name nameNode, Type type, bool inParam, bool isGhost, Expression defaultValue, + Attributes attributes = null, + bool isOld = false, bool isNameOnly = false, bool isOlder = false, string nameForCompilation = null) + : base(origin, nameNode, type, isGhost) { + Contract.Requires(origin != null); + Contract.Requires(nameNode != null); Contract.Requires(type != null); Contract.Requires(inParam || defaultValue == null); - Contract.Requires(!isNameOnly || (inParam && !name.StartsWith("#"))); + Contract.Requires(!isNameOnly || (inParam && !nameNode.Value.StartsWith("#"))); InParam = inParam; IsOld = isOld; DefaultValue = defaultValue; + Attributes = attributes; IsNameOnly = isNameOnly; IsOlder = isOlder; - NameForCompilation = nameForCompilation ?? name; + NameForCompilation = nameForCompilation ?? nameNode.Value; } - public bool HasName { - get { - return !Name.StartsWith("#"); - } + public bool HasName => !Name.StartsWith("#"); + + public override string GetOrCreateCompileName(CodeGenIdGenerator generator) { + return CompileName; } - private string sanitizedName; - public override string SanitizedName => - sanitizedName ??= SanitizeName(Name); // No unique-ification - public override string CompileName => - compileName ??= SanitizeName(NameForCompilation); + public string CompileName => compileName ??= SanitizeName(NameForCompilation); public override IEnumerable Children => (DefaultValue != null ? new List { DefaultValue } : Enumerable.Empty()).Concat(base.Children); @@ -52,9 +59,9 @@ public bool HasName { /// of each extreme lemma (for use in the extreme-method body only, not the specification). /// public class ImplicitFormal : Formal { - public ImplicitFormal(IToken tok, string name, Type type, bool inParam, bool isGhost) - : base(tok, name, type, inParam, isGhost, null) { - Contract.Requires(tok != null); + public ImplicitFormal(IOrigin origin, string name, Type type, bool inParam, bool isGhost) + : base(origin, name, type, inParam, isGhost, null, null) { + Contract.Requires(origin != null); Contract.Requires(name != null); Contract.Requires(type != null); } @@ -67,9 +74,9 @@ public ImplicitFormal(IToken tok, string name, Type type, bool inParam, bool isG /// implementation. /// public class ThisSurrogate : ImplicitFormal { - public ThisSurrogate(IToken tok, Type type) - : base(tok, "this", type, true, false) { - Contract.Requires(tok != null); + public ThisSurrogate(IOrigin origin, Type type) + : base(origin, "this", type, true, false) { + Contract.Requires(origin != null); Contract.Requires(type != null); } } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/Variables/IVariable.cs b/Source/DafnyCore/AST/Expressions/Variables/IVariable.cs index 12f4e4a7bc5..c8f174a0788 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/IVariable.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/IVariable.cs @@ -19,14 +19,14 @@ string UniqueName { bool HasBeenAssignedUniqueName { // unique names are not assigned until the Translator; if you don't already know if that stage has run, this boolean method will tell you get; } - static FreshIdGenerator CompileNameIdGenerator = new FreshIdGenerator(); - string AssignUniqueName(FreshIdGenerator generator); - string SanitizedName { - get; - } - string CompileName { - get; - } + string AssignUniqueName(VerificationIdGenerator generator); + + /// + /// A name suitable for compilation, but without the unique identifier. + /// Useful to generate readable identifiers in the generated source code. + /// + string CompileNameShadowable { get; } + string GetOrCreateCompileName(CodeGenIdGenerator generator); PreType PreType { get; @@ -35,6 +35,13 @@ PreType PreType { Type Type { get; } + + /// + /// For a description of the difference between .Type and .UnnormalizedType, see Expression.UnnormalizedType. + /// + Type UnnormalizedType { + get; + } Type OptionalType { get; } diff --git a/Source/DafnyCore/AST/Expressions/Variables/IVariableContracts.cs b/Source/DafnyCore/AST/Expressions/Variables/IVariableContracts.cs index b2bb15930a1..7f803b3b0a1 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/IVariableContracts.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/IVariableContracts.cs @@ -1,10 +1,11 @@ using System; using System.Diagnostics.Contracts; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; namespace Microsoft.Dafny; [ContractClassFor(typeof(IVariable))] -public abstract class IVariableContracts : TokenNode, IVariable { +public abstract class IVariableContracts : NodeWithComputedRange, IVariable { public string Name { get { Contract.Ensures(Contract.Result() != null); @@ -34,19 +35,28 @@ public bool HasBeenAssignedUniqueName { throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here } } - public string SanitizedName { + public string SanitizedName(CodeGenIdGenerator generator) { + Contract.Ensures(Contract.Result() != null); + throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here + } + + public string CompileNameShadowable { get { Contract.Ensures(Contract.Result() != null); throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here } } - public string CompileName { + public string GetOrCreateCompileName(CodeGenIdGenerator generator) { + Contract.Ensures(Contract.Result() != null); + throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here + } + public Type Type { get { - Contract.Ensures(Contract.Result() != null); + Contract.Ensures(Contract.Result() != null); throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here } } - public Type Type { + public Type UnnormalizedType { get { Contract.Ensures(Contract.Result() != null); throw new NotImplementedException(); // this getter implementation is here only so that the Ensures contract can be given here @@ -74,13 +84,13 @@ public bool IsGhost { public void MakeGhost() { throw new NotImplementedException(); } - public string AssignUniqueName(FreshIdGenerator generator) { + public string AssignUniqueName(VerificationIdGenerator generator) { Contract.Ensures(Contract.Result() != null); throw new NotImplementedException(); } - public abstract IToken NameToken { get; } - public DafnySymbolKind Kind => throw new NotImplementedException(); + public abstract IOrigin NavigationToken { get; } + public SymbolKind? Kind => throw new NotImplementedException(); public string GetDescription(DafnyOptions options) { throw new NotImplementedException(); } diff --git a/Source/DafnyCore/AST/Expressions/Variables/IdentifierExpr.cs b/Source/DafnyCore/AST/Expressions/Variables/IdentifierExpr.cs index 1693a8e3a17..d0294f48f50 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/IdentifierExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/IdentifierExpr.cs @@ -4,7 +4,7 @@ namespace Microsoft.Dafny; -public class IdentifierExpr : Expression, IHasUsages, ICloneable { +public class IdentifierExpr : Expression, IHasReferences, ICloneable { [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Name != null); @@ -13,20 +13,20 @@ void ObjectInvariant() { public readonly string Name; [FilledInDuringResolution] public IVariable Var; - public string DafnyName => tok.line > 0 ? RangeToken.PrintOriginal() : Name; + public string DafnyName => Origin.line > 0 ? Origin.PrintOriginal() : Name; - public IdentifierExpr(IToken tok, string name) - : base(tok) { - Contract.Requires(tok != null); + public IdentifierExpr(IOrigin origin, string name) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(name != null); Name = name; } /// /// Constructs a resolved IdentifierExpr. /// - public IdentifierExpr(IToken tok, IVariable v) - : base(tok) { - Contract.Requires(tok != null); + public IdentifierExpr(IOrigin origin, IVariable v) + : base(origin) { + Contract.Requires(origin != null); Contract.Requires(v != null); Name = v.Name; Var = v; @@ -52,11 +52,10 @@ public static bool Is(Expression expr, IVariable variable) { return expr.Resolved is IdentifierExpr identifierExpr && identifierExpr.Var == variable; } - public IEnumerable GetResolvedDeclarations() { - return Enumerable.Repeat(Var, 1); + public IEnumerable GetReferences() { + return Enumerable.Repeat(new Reference(Origin, Var), 1); } - public IToken NameToken => tok; public override IEnumerable Children { get; } = Enumerable.Empty(); } @@ -65,14 +64,14 @@ public IEnumerable GetResolvedDeclarations() { /// assigning a value to a Method's out parameter. /// public class ImplicitIdentifierExpr : IdentifierExpr { - public ImplicitIdentifierExpr(IToken tok, string name) - : base(tok, name) { } + public ImplicitIdentifierExpr(IOrigin origin, string name) + : base(origin, name) { } /// /// Constructs a resolved implicit identifier. /// - public ImplicitIdentifierExpr(IToken tok, IVariable v) - : base(tok, v) { } + public ImplicitIdentifierExpr(IOrigin origin, IVariable v) + : base(origin, v) { } public override bool IsImplicit => true; } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/Variables/LetExpr.cs b/Source/DafnyCore/AST/Expressions/Variables/LetExpr.cs index 849d30f43c7..07a83ddb972 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/LetExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/LetExpr.cs @@ -9,20 +9,20 @@ public class LetExpr : Expression, IAttributeBearingDeclaration, IBoundVarsBeari public readonly List RHSs; public readonly Expression Body; public readonly bool Exact; // Exact==true means a regular let expression; Exact==false means an assign-such-that expression - public readonly Attributes Attributes; - Attributes IAttributeBearingDeclaration.Attributes => Attributes; - [FilledInDuringResolution] public List Constraint_Bounds; // null for Exact=true and for when expression is in a ghost context + public Attributes Attributes { get; set; } + string IAttributeBearingDeclaration.WhatKind => "let expression"; + [FilledInDuringResolution] public List Constraint_Bounds; // null for Exact=true and for when expression is in a ghost context // invariant Constraint_Bounds == null || Constraint_Bounds.Count == BoundVars.Count; private Expression translationDesugaring; // filled in during translation, lazily; to be accessed only via Translation.LetDesugaring; always null when Exact==true - private Translator lastTranslatorUsed; // avoid clashing desugaring between translators + private BoogieGenerator lastBoogieGeneratorUsed; // avoid clashing desugaring between translators - public void SetTranslationDesugaring(Translator trans, Expression expr) { - lastTranslatorUsed = trans; + public void SetTranslationDesugaring(BoogieGenerator trans, Expression expr) { + lastBoogieGeneratorUsed = trans; translationDesugaring = expr; } - public Expression GetTranslationDesugaring(Translator trans) { - if (lastTranslatorUsed == trans) { + public Expression GetTranslationDesugaring(BoogieGenerator trans) { + if (lastBoogieGeneratorUsed == trans) { return translationDesugaring; } else { return null; @@ -44,8 +44,8 @@ public LetExpr(Cloner cloner, LetExpr original) : base(cloner, original) { } } - public LetExpr(IToken tok, List> lhss, List rhss, Expression body, bool exact, Attributes attrs = null) - : base(tok) { + public LetExpr(IOrigin origin, List> lhss, List rhss, Expression body, bool exact, Attributes attrs = null) + : base(origin) { LHSs = lhss; RHSs = rhss; Body = body; @@ -53,7 +53,7 @@ public LetExpr(IToken tok, List> lhss, List rh Attributes = attrs; } - public static LetExpr Havoc(IToken tok, Type type = null) { + public static LetExpr Havoc(IOrigin tok, Type type = null) { type ??= new InferredTypeProxy(); var boundVar = new BoundVar(tok, "x", type); var casePatterns = new List>() { new(tok, boundVar) }; @@ -123,8 +123,8 @@ public IEnumerable BoundVars { public IEnumerable AllBoundVars => BoundVars; public override IEnumerable Children => - (Attributes != null ? new List { Attributes } : Enumerable.Empty()) - .Concat(LHSs) + Attributes.AsEnumerable(). + Concat(LHSs) .Concat(base.Children); public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { diff --git a/Source/DafnyCore/AST/Expressions/Variables/LetOrFailExpr.cs b/Source/DafnyCore/AST/Expressions/Variables/LetOrFailExpr.cs index c01fc64e877..a5a9529cd80 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/LetOrFailExpr.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/LetOrFailExpr.cs @@ -8,7 +8,7 @@ public class LetOrFailExpr : ConcreteSyntaxExpression, ICloneable public readonly Expression Rhs; public readonly Expression Body; - public LetOrFailExpr(IToken tok, CasePattern/*?*/ lhs, Expression rhs, Expression body) : base(tok) { + public LetOrFailExpr(IOrigin origin, CasePattern/*?*/ lhs, Expression rhs, Expression body) : base(origin) { Lhs = lhs; Rhs = rhs; Body = body; diff --git a/Source/DafnyCore/AST/Expressions/Variables/NonglobalVariable.cs b/Source/DafnyCore/AST/Expressions/Variables/NonglobalVariable.cs index ac3c520ac28..b4f14067005 100644 --- a/Source/DafnyCore/AST/Expressions/Variables/NonglobalVariable.cs +++ b/Source/DafnyCore/AST/Expressions/Variables/NonglobalVariable.cs @@ -1,32 +1,42 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; namespace Microsoft.Dafny; -public abstract class NonglobalVariable : TokenNode, IVariable { - readonly string name; +public abstract class NonglobalVariable : NodeWithComputedRange, IVariable { + public Name NameNode { get; } + + protected NonglobalVariable(IOrigin origin, Name nameNode, Type type, bool isGhost) : base(origin) { + Contract.Requires(origin != null); + Contract.Requires(nameNode != null); + Contract.Requires(type != null); + this.NameNode = nameNode; + IsTypeExplicit = type != null; + this.type = type ?? new InferredTypeProxy(); + this.isGhost = isGhost; + } [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(name != null); Contract.Invariant(type != null); } public string Name { get { Contract.Ensures(Contract.Result() != null); - return name; + return NameNode.Value; } } - public string DafnyName => RangeToken == null || tok.line == 0 ? Name : RangeToken.PrintOriginal(); + public string DafnyName => Origin == null || Origin.line == 0 ? Name : Origin.PrintOriginal(); public string DisplayName => LocalVariable.DisplayNameHelper(this); private string uniqueName; public string UniqueName => uniqueName; public bool HasBeenAssignedUniqueName => uniqueName != null; - public string AssignUniqueName(FreshIdGenerator generator) { + public string AssignUniqueName(VerificationIdGenerator generator) { return uniqueName ??= generator.FreshId(Name + "#"); } @@ -73,16 +83,19 @@ public static string SanitizeName(string nm) { } } - private string sanitizedName; - public virtual string SanitizedName => - sanitizedName ??= $"_{IVariable.CompileNameIdGenerator.FreshNumericId()}_{SanitizeName(Name)}"; + private string sanitizedNameShadowable; + + public virtual string CompileNameShadowable => + sanitizedNameShadowable ??= SanitizeName(Name); protected string compileName; - public virtual string CompileName => - compileName ??= SanitizedName; + + public virtual string GetOrCreateCompileName(CodeGenIdGenerator generator) { + return compileName ??= $"_{generator.FreshNumericId()}_{CompileNameShadowable}"; + } Type type; - public bool IsTypeExplicit = false; + public bool IsTypeExplicit { get; set; } public Type SyntacticType { get { return type; } } // returns the non-normalized type public PreType PreType { get; set; } @@ -92,6 +105,16 @@ public Type Type { return type.Normalize(); } } + + /// + /// For a description of the difference between .Type and .UnnormalizedType, see Expression.UnnormalizedType. + /// + public Type UnnormalizedType { + get { + Contract.Ensures(Contract.Result() != null); + return type; + } + } Type IVariable.OptionalType { get { return Type; } // same as Type for NonglobalVariable } @@ -111,20 +134,10 @@ public void MakeGhost() { IsGhost = true; } - public NonglobalVariable(IToken tok, string name, Type type, bool isGhost) { - Contract.Requires(tok != null); - Contract.Requires(name != null); - Contract.Requires(type != null); - this.tok = tok; - this.name = name; - this.type = type; - this.isGhost = isGhost; - } - - public IToken NameToken => tok; + public IOrigin NavigationToken => NameNode.Origin; public override IEnumerable Children => IsTypeExplicit ? new List { Type } : Enumerable.Empty(); public override IEnumerable PreResolveChildren => IsTypeExplicit ? new List() { Type } : Enumerable.Empty(); - public DafnySymbolKind Kind => DafnySymbolKind.Variable; + public SymbolKind? Kind => SymbolKind.Variable; public string GetDescription(DafnyOptions options) { return this.AsText(); } diff --git a/Source/DafnyCore/AST/Expressions/Variables/ResolverIdentifierExpr.cs b/Source/DafnyCore/AST/Expressions/Variables/ResolverIdentifierExpr.cs new file mode 100644 index 00000000000..11de95a29f0 --- /dev/null +++ b/Source/DafnyCore/AST/Expressions/Variables/ResolverIdentifierExpr.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; + +namespace Microsoft.Dafny; + +/// +/// This class is used only inside the resolver itself. It gets hung in the AST in uncompleted name segments. +/// +class ResolverIdentifierExpr : Expression, IHasReferences, ICloneable { + public readonly TopLevelDecl Decl; + public readonly List TypeArgs; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(Decl != null); + Contract.Invariant(TypeArgs != null); + Contract.Invariant(TypeArgs.Count == Decl.TypeArgs.Count); + Contract.Invariant(Type is ResolverTypeModule or ResolverTypeType); + } + + public ResolverIdentifierExpr(Cloner cloner, ResolverIdentifierExpr original) : base(cloner, original) { + Decl = original.Decl; + TypeArgs = original.TypeArgs; + } + + public override IEnumerable Children => TypeArgs.SelectMany(ta => ta.Nodes); + + public abstract class ResolverType : Type { + public override bool ComputeMayInvolveReferences(ISet/*?*/ visitedDatatypes) { + return false; + } + public override Type Subst(IDictionary subst) { + throw new NotSupportedException(); + } + + public override Type ReplaceTypeArguments(List arguments) { + throw new NotSupportedException(); + } + } + public class ResolverTypeModule : ResolverType { + [System.Diagnostics.Contracts.Pure] + public override string TypeName(DafnyOptions options, ModuleDefinition context, bool parseAble) { + Contract.Assert(parseAble == false); + return "#module"; + } + public override bool Equals(Type that, bool keepConstraints = false) { + return that.NormalizeExpand(keepConstraints) is ResolverTypeModule; + } + } + public class ResolverTypeType : ResolverType { + [System.Diagnostics.Contracts.Pure] + public override string TypeName(DafnyOptions options, ModuleDefinition context, bool parseAble) { + Contract.Assert(parseAble == false); + return "#type"; + } + public override bool Equals(Type that, bool keepConstraints = false) { + return that.NormalizeExpand(keepConstraints) is ResolverTypeType; + } + } + + public ResolverIdentifierExpr(IOrigin origin, TopLevelDecl decl, List typeArgs) + : base(origin) { + Contract.Requires(origin != null); + Contract.Requires(decl != null); + Contract.Requires(typeArgs != null && typeArgs.Count == decl.TypeArgs.Count); + Decl = decl; + TypeArgs = typeArgs; + Type = decl is ModuleDecl ? (Type)new ResolverTypeModule() : new ResolverTypeType(); + PreType = decl is ModuleDecl ? new PreTypePlaceholderModule() : new PreTypePlaceholderType(); + } + public ResolverIdentifierExpr(IOrigin origin, TypeParameter tp) + : this(origin, tp, new List()) { + Contract.Requires(origin != null); + Contract.Requires(tp != null); + } + + public IEnumerable GetReferences() { + return new[] { new Reference(Center, Decl) }; + } + + public ResolverIdentifierExpr Clone(Cloner cloner) { + return new ResolverIdentifierExpr(cloner, this); + } +} \ No newline at end of file diff --git a/Source/DafnyCore/AST/Expressions/Variables/Resolver_IdentifierExpr.cs b/Source/DafnyCore/AST/Expressions/Variables/Resolver_IdentifierExpr.cs deleted file mode 100644 index e781b702630..00000000000 --- a/Source/DafnyCore/AST/Expressions/Variables/Resolver_IdentifierExpr.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Linq; - -namespace Microsoft.Dafny; - -/// -/// This class is used only inside the resolver itself. It gets hung in the AST in uncompleted name segments. -/// -class Resolver_IdentifierExpr : Expression, IHasUsages, ICloneable { - public readonly TopLevelDecl Decl; - public readonly List TypeArgs; - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(Decl != null); - Contract.Invariant(TypeArgs != null); - Contract.Invariant(TypeArgs.Count == Decl.TypeArgs.Count); - Contract.Invariant(Type is ResolverType_Module || Type is ResolverType_Type); - } - - public Resolver_IdentifierExpr(Cloner cloner, Resolver_IdentifierExpr original) : base(cloner, original) { - Decl = original.Decl; - TypeArgs = original.TypeArgs; - } - - public override IEnumerable Children => TypeArgs.SelectMany(ta => ta.Nodes); - - public abstract class ResolverType : Type { - public override bool ComputeMayInvolveReferences(ISet/*?*/ visitedDatatypes) { - return false; - } - public override Type Subst(IDictionary subst) { - throw new NotSupportedException(); - } - - public override Type ReplaceTypeArguments(List arguments) { - throw new NotSupportedException(); - } - } - public class ResolverType_Module : ResolverType { - [System.Diagnostics.Contracts.Pure] - public override string TypeName(DafnyOptions options, ModuleDefinition context, bool parseAble) { - Contract.Assert(parseAble == false); - return "#module"; - } - public override bool Equals(Type that, bool keepConstraints = false) { - return that.NormalizeExpand(keepConstraints) is ResolverType_Module; - } - } - public class ResolverType_Type : ResolverType { - [System.Diagnostics.Contracts.Pure] - public override string TypeName(DafnyOptions options, ModuleDefinition context, bool parseAble) { - Contract.Assert(parseAble == false); - return "#type"; - } - public override bool Equals(Type that, bool keepConstraints = false) { - return that.NormalizeExpand(keepConstraints) is ResolverType_Type; - } - } - - public Resolver_IdentifierExpr(IToken tok, TopLevelDecl decl, List typeArgs) - : base(tok) { - Contract.Requires(tok != null); - Contract.Requires(decl != null); - Contract.Requires(typeArgs != null && typeArgs.Count == decl.TypeArgs.Count); - Decl = decl; - TypeArgs = typeArgs; - Type = decl is ModuleDecl ? (Type)new ResolverType_Module() : new ResolverType_Type(); - PreType = decl is ModuleDecl ? new PreTypePlaceholderModule() : new PreTypePlaceholderType(); - } - public Resolver_IdentifierExpr(IToken tok, TypeParameter tp) - : this(tok, tp, new List()) { - Contract.Requires(tok != null); - Contract.Requires(tp != null); - } - - public IEnumerable GetResolvedDeclarations() { - return new[] { Decl }; - } - - public IToken NameToken => tok; - public Resolver_IdentifierExpr Clone(Cloner cloner) { - return new Resolver_IdentifierExpr(cloner, this); - } -} \ No newline at end of file diff --git a/Source/DafnyCore/AST/ExtremeCloner.cs b/Source/DafnyCore/AST/ExtremeCloner.cs index d4d725e82e3..db247b445dc 100644 --- a/Source/DafnyCore/AST/ExtremeCloner.cs +++ b/Source/DafnyCore/AST/ExtremeCloner.cs @@ -17,7 +17,7 @@ protected ExtremeCloner(Expression k, ErrorReporter reporter) { Contract.Requires(reporter != null); this.k = k; this.reporter = reporter; - this.suffix = string.Format("#[{0}]", Printer.ExprToString(reporter.Options, k)); + this.suffix = $"#[{Printer.ExprToString(reporter.Options, k)}]"; } protected Expression CloneCallAndAddK(ApplySuffix e) { Contract.Requires(e != null); @@ -31,19 +31,19 @@ protected Expression CloneCallAndAddK(ApplySuffix e) { string name; if (e.Lhs is NameSegment ns) { name = ns.Name; - lhs = new NameSegment(Tok(ns.tok), name + "#", ns.OptTypeArguments?.ConvertAll(CloneType)); + lhs = new NameSegment(Origin(ns.Origin), name + "#", ns.OptTypeArguments?.ConvertAll(CloneType)); } else { var edn = (ExprDotName)e.Lhs; name = edn.SuffixName; - lhs = new ExprDotName(Tok(edn.tok), CloneExpr(edn.Lhs), name + "#", edn.OptTypeArguments?.ConvertAll(CloneType)); + lhs = new ExprDotName(Origin(edn.Origin), CloneExpr(edn.Lhs), new Name(name + "#"), edn.OptTypeArguments?.ConvertAll(CloneType)); } var args = new List(); args.Add(new ActualBinding(null, k)); foreach (var arg in e.Bindings.ArgumentBindings) { args.Add(CloneActualBinding(arg)); } - var apply = new ApplySuffix(Tok(e.tok), e.AtTok == null ? null : Tok(e.AtTok), lhs, args, Tok(e.CloseParen)); - reporter.Info(MessageSource.Cloner, e.tok, name + suffix); + var apply = new ApplySuffix(Origin(e.Origin), e.AtTok == null ? null : Origin(e.AtTok), lhs, args, e.CloseParen); + reporter.Info(MessageSource.Cloner, e.Origin, name + suffix); return apply; } @@ -59,8 +59,20 @@ protected Expression CloneCallAndAddK(FunctionCallExpr e) { foreach (var binding in e.Bindings.ArgumentBindings) { args.Add(CloneActualBinding(binding)); } - var fexp = new FunctionCallExpr(Tok(e.tok), e.Name + "#", receiver, e.OpenParen, e.CloseParen, args, e.AtLabel); - reporter.Info(MessageSource.Cloner, e.tok, e.Name + suffix); + var fexp = new FunctionCallExpr(Origin(e.Origin), e.NameNode.Append("#"), receiver, e.OpenParen, e.CloseParen, args, e.AtLabel); + reporter.Info(MessageSource.Cloner, e.Origin, e.Name + suffix); return fexp; } + + protected Expression CloneEqualityAndAddK(BinaryExpr binaryExpr) { + if (this.CloneResolvedFields) { + throw new NotImplementedException(); + } + + var eq = new TernaryExpr(Origin(binaryExpr.Origin), + binaryExpr.ResolvedOp == BinaryExpr.ResolvedOpcode.EqCommon ? TernaryExpr.Opcode.PrefixEqOp : TernaryExpr.Opcode.PrefixNeqOp, + k, CloneExpr(binaryExpr.E0), CloneExpr(binaryExpr.E1)); + reporter.Info(MessageSource.Cloner, binaryExpr.Origin, "==" + suffix); + return eq; + } } diff --git a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs index f71835f6ce2..cd04febe3ad 100644 --- a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs +++ b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs @@ -11,42 +11,31 @@ namespace Microsoft.Dafny; class ExtremeLemmaBodyCloner : ExtremeCloner { readonly ExtremeLemma context; readonly ISet focalPredicates; - public ExtremeLemmaBodyCloner(ExtremeLemma context, Expression k, ISet focalPredicates, ErrorReporter reporter) + readonly ISet focalCodatatypeEquality; + public ExtremeLemmaBodyCloner(ExtremeLemma context, Expression k, + ISet focalPredicates, ISet focalCodatatypeEquality, ErrorReporter reporter) : base(k, reporter) { Contract.Requires(context != null); Contract.Requires(k != null); Contract.Requires(reporter != null); this.context = context; this.focalPredicates = focalPredicates; + this.focalCodatatypeEquality = focalCodatatypeEquality; } public override Expression CloneExpr(Expression expr) { if (reporter.Options.RewriteFocalPredicates) { - if (expr is FunctionCallExpr) { - var e = (FunctionCallExpr)expr; -#if DEBUG_PRINT - if (e.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => e.Function.Name == p.Name + "#")) { - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(e)); - } -#endif + if (expr is FunctionCallExpr functionCallExpr) { // Note, we don't actually ever get here, because all calls will have been parsed as ApplySuffix. // However, if something changes in the future (for example, some rewrite that changing an ApplySuffix // to its resolved FunctionCallExpr), then we do want this code, so with the hope of preventing // some error in the future, this case is included. (Of course, it is currently completely untested!) - var f = e.Function as ExtremePredicate; - if (f != null && focalPredicates.Contains(f)) { -#if DEBUG_PRINT - var r = CloneCallAndAddK(e); - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(r)); - return r; -#else - return CloneCallAndAddK(e); -#endif + if (functionCallExpr.Function is ExtremePredicate f && focalPredicates.Contains(f)) { + return CloneCallAndAddK(functionCallExpr); } } else if (expr is StaticReceiverExpr ee) { - return new StaticReceiverExpr(Tok(ee.tok), ee.Type, ee.IsImplicit); - } else if (expr is ApplySuffix) { - var apply = (ApplySuffix)expr; + return new StaticReceiverExpr(Origin(ee.Origin), ee.Type, ee.IsImplicit); + } else if (expr is ApplySuffix apply) { if (!apply.WasResolved()) { // Since we're assuming the enclosing statement to have been resolved, this ApplySuffix must // be part of an ExprRhs that actually designates a method call. Such an ApplySuffix does @@ -54,56 +43,46 @@ public override Expression CloneExpr(Expression expr) { var mse = (MemberSelectExpr)apply.Lhs.Resolved; Contract.Assume(mse.Member is Method); } else { - var fce = apply.Resolved as FunctionCallExpr; - if (fce != null) { -#if DEBUG_PRINT - if (fce.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => fce.Function.Name == p.Name + "#")) { - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(fce)); - } -#endif - var f = fce.Function as ExtremePredicate; - if (f != null && focalPredicates.Contains(f)) { -#if DEBUG_PRINT - var r = CloneCallAndAddK(fce); - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(r)); - return r; -#else + if (apply.Resolved is FunctionCallExpr fce) { + if (fce.Function is ExtremePredicate f && focalPredicates.Contains(f)) { return CloneCallAndAddK(fce); -#endif } } } + } else if (expr is BinaryExpr { ResolvedOp: BinaryExpr.ResolvedOpcode.EqCommon or BinaryExpr.ResolvedOpcode.NeqCommon } binaryExpr) { + if (binaryExpr.E0.Type.AsCoDatatype is { } coDatatypeDecl && focalCodatatypeEquality.Contains(coDatatypeDecl)) { + return CloneEqualityAndAddK(binaryExpr); + } } } return base.CloneExpr(expr); } public override AssignmentRhs CloneRHS(AssignmentRhs rhs) { - var r = rhs as ExprRhs; - if (r != null && r.Expr is ApplySuffix) { - var apply = (ApplySuffix)r.Expr; - var mse = apply.Lhs.Resolved as MemberSelectExpr; - if (mse != null && mse.Member is ExtremeLemma && ModuleDefinition.InSameSCC(context, (ExtremeLemma)mse.Member)) { + if (rhs is ExprRhs { Expr: ApplySuffix apply }) { + if (apply.Lhs.Resolved is MemberSelectExpr { Member: ExtremeLemma extremeLemma } && ModuleDefinition.InSameSCC(context, extremeLemma)) { // we're looking at a recursive call to an extreme lemma - Contract.Assert(apply.Lhs is NameSegment || apply.Lhs is ExprDotName); // this is the only way a call statement can have been parsed + Contract.Assert(apply.Lhs is NameSegment or ExprDotName); // this is the only way a call statement can have been parsed // clone "apply.Lhs", changing the least/greatest lemma to the prefix lemma; then clone "apply", adding in the extra argument Expression lhsClone; if (apply.Lhs is NameSegment) { var lhs = (NameSegment)apply.Lhs; - lhsClone = new NameSegment(Tok(lhs.tok), lhs.Name + "#", lhs.OptTypeArguments == null ? null : lhs.OptTypeArguments.ConvertAll(CloneType)); + lhsClone = new NameSegment(Origin(lhs.Origin), lhs.Name + "#", lhs.OptTypeArguments?.ConvertAll(CloneType)); } else { var lhs = (ExprDotName)apply.Lhs; - lhsClone = new ExprDotName(Tok(lhs.tok), CloneExpr(lhs.Lhs), lhs.SuffixName + "#", lhs.OptTypeArguments == null ? null : lhs.OptTypeArguments.ConvertAll(CloneType)); + lhsClone = new ExprDotName(Origin(lhs.Origin), CloneExpr(lhs.Lhs), lhs.SuffixNameNode.Append("#"), lhs.OptTypeArguments?.ConvertAll(CloneType)); } + var args = new List(); args.Add(new ActualBinding(null, k)); apply.Bindings.ArgumentBindings.ForEach(arg => args.Add(CloneActualBinding(arg))); - var applyClone = new ApplySuffix(Tok(apply.tok), apply.AtTok == null ? null : Tok(apply.AtTok), - lhsClone, args, Tok(apply.CloseParen)); + var applyClone = new ApplySuffix(Origin(apply.Origin), apply.AtTok == null ? null : Origin(apply.AtTok), + lhsClone, args, apply.CloseParen); var c = new ExprRhs(applyClone, CloneAttributes(rhs.Attributes)); - reporter.Info(MessageSource.Cloner, apply.Lhs.tok, mse.Member.Name + suffix); + reporter.Info(MessageSource.Cloner, apply.Lhs.Origin, extremeLemma.Name + suffix); return c; } } + return base.CloneRHS(rhs); } } diff --git a/Source/DafnyCore/AST/ExtremeLemmaSpecificationSubstituter.cs b/Source/DafnyCore/AST/ExtremeLemmaSpecificationSubstituter.cs index 0ffa7e0b7aa..ced594ebff9 100644 --- a/Source/DafnyCore/AST/ExtremeLemmaSpecificationSubstituter.cs +++ b/Source/DafnyCore/AST/ExtremeLemmaSpecificationSubstituter.cs @@ -53,9 +53,9 @@ public override Expression CloneExpr(Expression expr) { var op = e.ResolvedOp == BinaryExpr.ResolvedOpcode.EqCommon ? TernaryExpr.Opcode.PrefixEqOp : TernaryExpr.Opcode.PrefixNeqOp; var A = CloneExpr(e.E0); var B = CloneExpr(e.E1); - var teq = new TernaryExpr(Tok(e.tok), op, k, A, B); + var teq = new TernaryExpr(Origin(e.Origin), op, k, A, B); var opString = op == TernaryExpr.Opcode.PrefixEqOp ? "==" : "!="; - reporter.Info(MessageSource.Cloner, e.tok, opString + suffix); + reporter.Info(MessageSource.Cloner, e.Origin, opString + suffix); return teq; } } @@ -66,7 +66,7 @@ public override Type CloneType(Type t) { var tt = (UserDefinedType)t; // We want syntactic cloning of the Expression that is tt.NamePath, unlike the semantic (that is, post-resolved) // cloning that CloneExpr is doing above. - return new UserDefinedType(Tok(tt.tok), CloneNamePathExpression(tt.NamePath)); + return new UserDefinedType(Origin(tt.Origin), CloneNamePathExpression(tt.NamePath)); } else { return base.CloneType(t); } @@ -78,7 +78,7 @@ Expression CloneNamePathExpression(Expression expr) { return new NameSegment(this, e); } else { var e = (ExprDotName)expr; - return new ExprDotName(Tok(e.tok), CloneNamePathExpression(e.Lhs), e.SuffixName, e.OptTypeArguments == null ? null : e.OptTypeArguments.ConvertAll(CloneType)); + return new ExprDotName(Origin(e.Origin), CloneNamePathExpression(e.Lhs), e.SuffixNameNode, e.OptTypeArguments == null ? null : e.OptTypeArguments.ConvertAll(CloneType)); } } } diff --git a/Source/DafnyCore/AST/Formatting.dfy b/Source/DafnyCore/AST/Formatting.dfy index 0cabc68c628..283c9156da5 100644 --- a/Source/DafnyCore/AST/Formatting.dfy +++ b/Source/DafnyCore/AST/Formatting.dfy @@ -9,36 +9,37 @@ include "System.dfy" include "DafnyInterface.dfy" // Provided new Dafny code (trait Microsoft.Dafny.Formatting.IIndentationFormatter) -module {:extern "Microsoft"} {:options "-functionSyntax:4"} Microsoft { +@Options("-functionSyntax:4") +module {:extern "Microsoft"} Microsoft { module {:extern "Dafny"} Dafny { module {:extern "Formatting"} Formatting { import opened MicrosoftDafny import opened System - const {:extern "System", "String.Empty"} CsStringEmpty: CsString; + const {:extern "System", "String.Empty"} CsStringEmpty: CsString trait IIndentationFormatter { // Given the current indentation at this point // returns the leading trivia but with its indentation corrected. - function GetNewLeadingTrivia(token: IToken): CsString + function GetNewLeadingTrivia(token: Token): CsString // Given the current indentation at this point // returns the trailing trivia but with its indentation corrected. - function GetNewTrailingTrivia(token: IToken): CsString + function GetNewTrailingTrivia(token: Token): CsString } lemma IsAllocated(x: T) ensures allocated(x) { } - /** Prints the entire program while fixing identation, based on + /** Prints the entire program while fixing indentation, based on 1) indentation information provided by the IIndentationFormatter reindent 2) Reindentation algorithm provided by the same reindent */ - method ReindentProgramFromFirstToken(firstToken: IToken, reindent: IIndentationFormatter) returns (s: CsString) + method ReindentProgramFromFirstToken(firstToken: Token, reindent: IIndentationFormatter) returns (s: CsString) requires firstToken.Valid() - ensures forall token <- firstToken.allTokens :: s.Contains(token.val) + ensures forall token: Token <- firstToken.allTokens :: s.Contains(token.val) { - var token: IToken? := firstToken; + var token: Token? := firstToken; var sb := new CsStringBuilder(); ghost var i := 0; var allTokens := firstToken.allTokens; @@ -49,7 +50,7 @@ module {:extern "Microsoft"} {:options "-functionSyntax:4"} Microsoft { && token.Valid() && i < |allTokens| && token == allTokens[i] - invariant forall t <- allTokens[0..i] :: sb.built.Contains(t.val) + invariant forall t: Token <- allTokens[0..i] :: sb.built.Contains(t.val) { if(token.Next == null) { firstToken.TokenNextIsNullImpliesLastToken(token, i); @@ -65,7 +66,7 @@ module {:extern "Microsoft"} {:options "-functionSyntax:4"} Microsoft { sb.Append(token.val); sb.Append(newTrailingTrivia); ContainsTransitive(); - assert {:split_here} forall t <- allTokens[0..i+1] :: sb.built.Contains(t.val); + assert {:split_here} forall t: Token <- allTokens[0..i+1] :: sb.built.Contains(t.val); token := token.Next; i := i + 1; } diff --git a/Source/DafnyCore/AST/Grammar/IFileSystem.cs b/Source/DafnyCore/AST/Grammar/IFileSystem.cs index 5af553b872d..8f078e965c0 100644 --- a/Source/DafnyCore/AST/Grammar/IFileSystem.cs +++ b/Source/DafnyCore/AST/Grammar/IFileSystem.cs @@ -1,24 +1,56 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; +using DafnyCore.Options; +using Microsoft.Dafny.LanguageServer.Workspace; using Microsoft.Extensions.FileSystemGlobbing.Abstractions; namespace Microsoft.Dafny; +public record FileSnapshot(TextReader Reader, int? Version); + public interface IFileSystem { - TextReader ReadFile(Uri uri); + FileSnapshot ReadFile(Uri uri); public bool Exists(Uri path); DirectoryInfoBase GetDirectoryInfoBase(string root); } +public class InMemoryFileSystem : IFileSystem { + private readonly IReadOnlyDictionary files; + + public InMemoryFileSystem(IReadOnlyDictionary files) { + this.files = files; + } + + public FileSnapshot ReadFile(Uri uri) { + if (files.TryGetValue(uri, out var entry)) { + return new FileSnapshot(new StringReader(entry), null); + } + + return OnDiskFileSystem.Instance.ReadFile(uri); + } + + public bool Exists(Uri path) { + return files.ContainsKey(path) || OnDiskFileSystem.Instance.Exists(path); + } + + public DirectoryInfoBase GetDirectoryInfoBase(string root) { + var inMemoryFiles = files.Keys.Select(openFileUri => openFileUri.LocalPath); + var inMemory = new InMemoryDirectoryInfoFromDotNet8(root, inMemoryFiles); + return new CombinedDirectoryInfo(new[] { inMemory, OnDiskFileSystem.Instance.GetDirectoryInfoBase(root) }); + } +} + public class OnDiskFileSystem : IFileSystem { public static readonly IFileSystem Instance = new OnDiskFileSystem(); private OnDiskFileSystem() { } - public TextReader ReadFile(Uri uri) { - return new StreamReader(uri.LocalPath); + public FileSnapshot ReadFile(Uri uri) { + return new FileSnapshot(new StreamReader(uri.LocalPath), null); } public bool Exists(Uri path) { diff --git a/Source/DafnyCore/AST/Grammar/IndentationFormatter.cs b/Source/DafnyCore/AST/Grammar/IndentationFormatter.cs index 0256db2fb27..7f98214ac91 100644 --- a/Source/DafnyCore/AST/Grammar/IndentationFormatter.cs +++ b/Source/DafnyCore/AST/Grammar/IndentationFormatter.cs @@ -23,11 +23,15 @@ public IndentationFormatter(TokenNewIndentCollector formatter) { /// Creates an IndentationFormatter for the given program, /// by immediately processing all nodes and assigning indentations to most structural tokens /// - public static IndentationFormatter ForProgram(Program program, bool reduceBlockiness = true) { - var tokenNewIndentCollector = new TokenNewIndentCollector(program) { + public static IndentationFormatter ForProgram(Program program, Uri fileToFormat, bool reduceBlockiness = true) { + var tokenNewIndentCollector = new TokenNewIndentCollector(program, fileToFormat) { ReduceBlockiness = reduceBlockiness }; foreach (var child in program.DefaultModuleDef.PreResolveChildren) { + var isPhysicalToken = child.Origin.line != 0; + if (isPhysicalToken && child.Origin.Uri != fileToFormat) { + continue; + } if (child is TopLevelDecl topLevelDecl) { tokenNewIndentCollector.SetDeclIndentation(topLevelDecl, 0); } else if (child is Include include) { @@ -43,11 +47,11 @@ public static IndentationFormatter ForProgram(Program program, bool reduceBlocki #region Override for implementing IIndentationFormatter - public string GetNewLeadingTrivia(IToken token) { + public string GetNewLeadingTrivia(Token token) { return GetNewTrivia(token, false); } - public string GetNewTrailingTrivia(IToken token) { + public string GetNewTrailingTrivia(Token token) { return GetNewTrivia(token, true); } @@ -144,7 +148,7 @@ public string GetNewTrailingTrivia(IToken token) { /// // One last word /// } /// ``` - public string GetNewTrivia(IToken token, bool trailingTrivia) { + public string GetNewTrivia(Token token, bool trailingTrivia) { var precededByNewline = token.Prev != null && !trailingTrivia && TriviaFormatterHelper.EndsWithNewline(token.Prev.TrailingTrivia); if (token.val == "") { return trailingTrivia ? token.TrailingTrivia : token.LeadingTrivia; @@ -225,7 +229,7 @@ public string GetNewTrivia(IToken token, bool trailingTrivia) { }); } - private string ReIndentMultilineComment(IToken token, string capturedComment, int currentIndent, + private string ReIndentMultilineComment(IOrigin token, string capturedComment, int currentIndent, string indentationBefore, bool precededByNewline, out bool previousMatchWasSingleLineCommentToAlign) { var doubleStar = capturedComment.StartsWith("/**") && !capturedComment.StartsWith("/***"); @@ -293,7 +297,7 @@ private string ReIndentMultilineComment(IToken token, string capturedComment, in return Whitespace(currentIndent + relativeIndent - initialRelativeIndent) + result; } - private string ReIndentSingleLineComment(IToken token, string capturedComment, int originalCommentIndent, + private string ReIndentSingleLineComment(Token token, string capturedComment, int originalCommentIndent, int currentIndent, int newCommentIndent, Group caseCommented, ref bool previousMatchWasSingleLineCommentToAlign, ref string indentationBefore) { if (capturedComment.StartsWith("///") && !capturedComment.StartsWith("////")) { diff --git a/Source/DafnyCore/AST/Grammar/ParseErrors.cs b/Source/DafnyCore/AST/Grammar/ParseErrors.cs index 6cb251de182..bc318beb0b3 100644 --- a/Source/DafnyCore/AST/Grammar/ParseErrors.cs +++ b/Source/DafnyCore/AST/Grammar/ParseErrors.cs @@ -11,6 +11,7 @@ public class ParseErrors { public enum ErrorId { // ReSharper disable once InconsistentNaming none, + p_extra_attributes, p_duplicate_modifier, p_abstract_not_allowed, p_no_ghost_for_by_method, @@ -95,6 +96,7 @@ public enum ErrorId { p_no_decreases_expressions_with_star, p_assert_needs_by_or_semicolon, p_deprecated_forall_with_no_bound_variables, + p_deprecated_forall_statement_with_parentheses_around_bound_variables, p_forall_with_ensures_must_have_body, p_deprecated_modify_statement_with_block, p_calc_operator_must_be_transitive, @@ -137,26 +139,39 @@ public enum ErrorId { p_deprecated_statement_refinement, p_internal_exception, p_file_has_no_code, - p_general_traits_beta, + p_general_traits_datatype, + p_general_traits_full, + p_decreases_without_to, + p_binding_in_decreases_to, + p_ghost_in_decreases_to, } static ParseErrors() { - + Add(ErrorId.p_extra_attributes, + @" +@-attributes are not supported here".TrimStart(), Remove(true, "Remove this @-attribute")); Add(ErrorId.p_duplicate_modifier, @" No Dafny modifier, such as [`abstract`, `static`, `ghost`](https://dafny.org/latest/DafnyRef/DafnyRef#sec-declaration-modifiers) may be repeated Such repetition would be superfluous even if allowed. -", Remove(true, "remove duplicate modifier")); +".TrimStart(), Remove(true, "remove duplicate modifier")); Add(ErrorId.p_abstract_not_allowed, @" Only modules may be declared abstract. -", Remove(true)); +".TrimStart(), Remove(true)); - Add(ErrorId.p_general_traits_beta, + Add(ErrorId.p_general_traits_datatype, @" -Use of traits as non-reference types is a beta feature. To engage, use /generalTraits:1. -", Remove(true)); +A newtype extending a trait is not generally supported. The option --general-traits=full causes +Dafny to allow them in the input, but is not recommended. +".TrimStart(), Remove(true)); + + Add(ErrorId.p_general_traits_full, + @" +Use of traits as non-reference types is supported, but is not yet the default. Until it becomes the +default, use --general--traits=datatype to enable it. +".TrimStart(), Remove(true)); Add(ErrorId.p_no_ghost_for_by_method, @" @@ -165,7 +180,7 @@ Functions with a [by method](https://dafny.org/latest/DafnyRef/DafnyRef#sec-func in ghost contexts the function body is used and in compiled contexts the by-method body is used. The `ghost` keyword is not permitted on the declaration. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_ghost_forbidden_default_3, @" @@ -175,7 +190,7 @@ the by-method body is used. The `ghost` keyword is not permitted on the `function` means a non-ghost function. See the discussion [here](https://dafny.org/latest/DafnyRef/DafnyRef#sec-function-syntax) for a discussion of options to control this feature. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_ghost_forbidden_default, @" @@ -185,37 +200,37 @@ a discussion of options to control this feature. `function` means a non-ghost function. See the discussion [here](https://dafny.org/latest/DafnyRef/DafnyRef#sec-function-syntax) for a discussion of options to control this feature. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_ghost_forbidden, @" Only some kinds of declarations can be declared `ghost`, most often functions, fields, and local declarations. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_no_static, @" Only some kinds of declarations can be declared 'static', most often fields, constants, methods, and functions, and only within classes. Modules and the declarations within them are already always static. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_no_opaque, @" Only some kinds of declarations can be declared 'opaque': const fields and the various kinds of functions. -", Remove(true)); +".TrimStart(), Remove(true)); // TODO - not used at present Add(ErrorId.p_deprecated_attribute, @" This attribute is obsolete and unmaintained. It will be removed from dafny in the future. -", Remove(true, "remove attribute")); +".TrimStart(), Remove(true, "remove attribute")); Add(ErrorId.p_literal_string_required, @" The value of an options attribute cannot be a computed expression. It must be a literal string. -", range => new List { +".TrimStart(), range => new List { OneAction("enclose in quotes", range, "\"" + range.PrintOriginal() + "\"") }); @@ -226,7 +241,7 @@ The value of an options attribute cannot be a computed expression. It must be a such identifiers are reserved for internal use. In match statements and expressions, an identifier that is a single underscore is used as a wild-card match. -", range => new List { +".TrimStart(), range => new List { OneAction("remove underscore", range, range.PrintOriginal().Substring(1)) }); @@ -236,7 +251,7 @@ that is a single underscore is used as a wild-card match. such identifiers are reserved for internal use. In match statements and expressions, an identifier that is a single underscore is used as a wild-card match. -", range => new List { +".TrimStart(), range => new List { OneAction("remove underscore", range, range.PrintOriginal().Substring(1)) }); @@ -245,7 +260,7 @@ that is a single underscore is used as a wild-card match. A bitvector type name is the characters 'bv' followed by a non-negative integer literal. However, dafny only supports widths up to the largest signed 32-bit integer (2147483647). -"); +".TrimStart()); Add(ErrorId.p_array_dimension_too_large, @" @@ -254,21 +269,21 @@ A multi-dimensional array type name is the characters 'array' followed by dimensions up to the largest signed 32-bit integer (2147483647). In practice (as of v3.9.1) dimensions of more than a few can take overly long to resolve ([Issue #3180](https://github.com/dafny-lang/dafny/issues/3180)). -"); +".TrimStart()); Add(ErrorId.p_superfluous_export, @" Although all top-level declarations are contained in an implicit top-level module, there is no syntax to import that module. Such an import would likely cause a circular module dependency error. If the implicit module cannot be imported, there is no point to any export declarations inside the implicit module. -", Remove(false, "remove export declaration")); +".TrimStart(), Remove(false, "remove export declaration")); Add(ErrorId.p_bad_module_decl, @" The [syntax for a module declaration](https://dafny.org/latest/DafnyRef/DafnyRef#sec-modules) is either `module M { ... }` or `module M refines N { ... }` with optional attributes after the `module` keyword. This error message often occurs if the `refines` keyword is misspelled. -", range => new List { +".TrimStart(), range => new List { OneAction("replace '" + range.PrintOriginal() + "' with 'refines'", range, "refines"), OneAction("remove '" + range.PrintOriginal() + "'", range, "", true) }); @@ -280,7 +295,7 @@ it can be either the name of the export set or associated with the `predicate` d The parser associates it with the `export`. To avoid this warning, do not put the `least` or `greatest` token on the same line as the `predicate` token. If you intend for the `least` to go with the predicate, change the order of the declarations. -"); // TODO - could use a quick fix +".TrimStart()); // TODO - could use a quick fix Add(ErrorId.p_extraneous_comma_in_export, @" @@ -288,50 +303,50 @@ it can be either the name of the export set or associated with the `predicate` d a comma-separated list of identifiers, but the clauses themselves are not separated by any delimiter. So in the example above, the comma after `a` is wrong in each export declaration. This mistake is easy to make when the clauses are on the same line. -", Remove(false, "remove comma")); +".TrimStart(), Remove(false, "remove comma")); Add(ErrorId.p_top_level_field, @" `var` declarations are used to declare mutable fields of classes, local variables in method bodies, and identifiers in let-expressions. But mutable field declarations are not permitted at the static module level, including in the (implicit) toplevel module. Rather, you may want the declaration to be a `const` declaration or you may want the mutable field to be declared in the body of a class. -", Replace("const")); +".TrimStart(), Replace("const")); Add(ErrorId.p_bad_datatype_refinement, // TODO - add a code action @" There are limitations on refining a datatype, namely that the set of constructors cannot be changed. It is only allowed to add members to the body of the datatype. -"); +".TrimStart()); Add(ErrorId.p_module_level_function_always_static, @" All names declared in a module (outside a class-like entity) are implicitly `static`. Dafny does not allow them to be explictly, redundantly, declared `static`. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_module_level_method_always_static, @" All names declared in a module (outside a class-like entity) are implicitly `static`. Dafny does not allow them to be explictly, redundantly, declared `static`. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_const_decl_missing_identifier, @" This error arises from a truncated declarations of a const field, namely just a const keyword. To correct the error, add an identifier and either or both a type and initializing expression (or remove the const keyword). -"); +".TrimStart()); Add(ErrorId.p_module_level_const_always_static, @" All names declared in a module (outside a class-like entity) are implicitly `static`. Dafny does not allow them to be explictly, redundantly, declared `static`. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_bad_const_initialize_op, @" Dafny's syntax for initialization of const fields uses `:=`, not `=`. In Dafny, `=` is used only in type definitions. -", Replace(":=")); +".TrimStart(), Replace(":=")); Add(ErrorId.p_const_is_missing_type_or_init, @" @@ -339,40 +354,40 @@ All names declared in a module (outside a class-like entity) are implicitly `sta or a right-hand-side expression, whose type is then the type of the declared identifier. So use syntax either like `const i: int` or `const i := 5` (or both together). -", InsertAfter(": int := 42", "add example")); +".TrimStart(), InsertAfter(": int := 42", "add example")); Add(ErrorId.p_misplaced_ellipsis_in_newtype, @" There are limitations on refining a newtype, namely that the base type cannot be changed. You can change an abstract type into a newtype, however. When refining a newtype by adding a body, the ... stands in place of both the '=' and the base type. -"); // TODO - add an action +".TrimStart()); // TODO - add an action Add(ErrorId.p_output_of_function_not_ghost, @" The output of a predicate or function cannot be ghost. It is implicitly ghost if the function is ghost itself. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_no_new_on_output_formals, @" The `new` modifier only applies to input parameters. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_no_nameonly_on_output_formals, @" The `nameonly` modifier only applies to input parameters. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_no_older_on_output_formals, @" The `older` modifier only applies to input parameters. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_var_decl_must_have_type, @" Because a mutable field does not have initializer, it must have a type (as in `var f: int`). `const` declarations may have initializers; if they do they do not need an explicit type. -", range => new List { +".TrimStart(), range => new List { OneAction("insert ': bool'", range, range.PrintOriginal() + ": bool"), OneAction("insert ': int'", range, range.PrintOriginal() + ": int") }); @@ -381,7 +396,7 @@ The output of a predicate or function cannot be ghost. @" Dafny does not allow field declarations to have initializers. They are initialized in constructors. Local variable declarations (which also begin with `var`) may have initializers. -", Remove(false, "remove := and expression")); +".TrimStart(), Remove(false, "remove := and expression")); // TODO - when there is capability to find constructors, enhance this with a code action that moves the initialization to the constructor Add(ErrorId.p_datatype_formal_is_not_id, @@ -390,7 +405,7 @@ Local variable declarations (which also begin with `var`) may have initializers. In datatype constructors the 'name :' is optional; one can just state the type. However, if there is a name, it may not be a typename. The formal parameter name should be a simple identifier that is not a reserved word. -", Replace("_")); +".TrimStart(), Replace("_")); Add(ErrorId.p_nameonly_must_have_parameter_name, @" @@ -399,7 +414,7 @@ The formal parameter name should be a simple identifier that is not a reserved w then the name must be given, as in `datatype D = D (i: int, nameonly j: int) {}` More detail is given [here](https://dafny.org/latest/DafnyRef/DafnyRef#sec-parameter-bindings). -", range => new List { +".TrimStart(), range => new List { OneAction("remove 'nameonly'", range, "", true), OneAction("insert '_:'", range, range.PrintOriginal() + " _:") }); @@ -411,21 +426,21 @@ its control flow produces (yields) a value, but the execution continues from tha To accentuate this difference, a `yield` statement is used to say when the next value of the iterator is ready, rather than a `return` statement. In addition, the declaration does not use `returns` to state the out-parameter, as a method would. Rather it has a `yields` clause. The example above is a valid example if `returns` is replaced by `yields`. -", Replace("yields")); +".TrimStart(), Replace("yields")); Add(ErrorId.p_type_parameter_variance_forbidden, @" [Type-parameter variance](https://dafny.org/latest/DafnyRef/DafnyRef#sec-type-parameter-variance) is specified by a `+`, `-`, `*` or `!` before the type-parameter name. Such designations are allowed in generic type declarations but not in generic method, function, or predicate declarations. -", Remove(false, "remove type parameter variance")); +".TrimStart(), Remove(false, "remove type parameter variance")); Add(ErrorId.p_unexpected_type_characteristic, @" [Type characteristics](https://dafny.org/latest/DafnyRef/DafnyRef#sec-type-parameters), indicated in parentheses after the type name, state properties of the otherwise uninterpreted or abstract type. The currently defined type characteristics are designated by `==` (equality-supporting), `0` (auto-initializable), `00` (non-empty), and `!new` (non-reference). -", Replacements(new[] { +".TrimStart(), Replacements(new[] { ("==", "replace with '==' - this type supports equality"), ("0", "replace with '0' - this type is auto-initializable"), ("00", "replace with '00' - this type is nonempty"), @@ -439,10 +454,10 @@ state properties of the otherwise uninterpreted or abstract type. They are given in a parentheses-enclosed, comma-separated list after the type name. The currently defined type characteristics are designated by `==` (equality - supporting), `0` (auto - initializable), `00` (non - empty), and `!new` (non - reference). -", range => +".TrimStart(), range => range.Prev.val == "," ? new List { - OneAction("remove comma", range.Prev.ToRange(), ""), + OneAction("remove comma", range.Prev, ""), OneAction("insert '=='", range, "==" + range.PrintOriginal()), OneAction("insert '0'", range, "0" + range.PrintOriginal()), OneAction("insert '00'", range, "00" + range.PrintOriginal()), @@ -461,7 +476,7 @@ [Type characteristics](https://dafny.org/latest/DafnyRef/DafnyRef#sec-type-param The currently defined type characteristics are designated by `==` (equality - supporting), `0` (auto - initializable), `00` (non - empty), and `!new` (non - reference). Type characteristics are given in a parentheses-enclosed, comma-separated list after the type name. -", Replacements(new[] { +".TrimStart(), Replacements(new[] { ("==", "replace with '==' - this type supports equality"), ("0", "replace with '0' - this type is auto-initializable"), ("00", "replace with '00' - this type is nonempty"), @@ -471,26 +486,26 @@ [Type characteristics](https://dafny.org/latest/DafnyRef/DafnyRef#sec-type-param Add(ErrorId.p_deprecated_colemma, @" The adjectives `least` and `greatest` for lemmas and functions are more consistent with the nomenclature for coinduction. -", Replace("greatest lemma")); +".TrimStart(), Replace("greatest lemma")); Add(ErrorId.p_deprecated_inductive_lemma, @" The adjectives `least` and `greatest` for lemmas and functions are more consistent with the nomenclature for coinduction. -", Replace("least")); +".TrimStart(), Replace("least")); Add(ErrorId.p_constructor_not_in_class, @" Constructors are methods that initialize class instances. That is, when a new instance of a class is being created, using the `new` object syntax, some constructor of the class is called, perhaps a default anonymous one. So, constructor declarations only make sense within classes. -", Replace("method")); +".TrimStart(), Replace("method")); Add(ErrorId.p_method_missing_name, @" A method declaration always requires an identifier between the `method` keyword and the `(` that starts the formal parameter list. This is the case even when, as in the example above, a name is specified using `:extern`. The extern name is only used in the compiled code; it is not the name used to refer to the method in Dafny code -", InsertBefore("M")); +".TrimStart(), InsertBefore("M")); Add(ErrorId.p_extraneous_k, @" @@ -498,7 +513,7 @@ Least and greatest lemmas and predicates have a special parameter named `k`. Its type is specified in square brackets between the lemma/predicate name and the rest of the signature. The type may be either `nat` or `ORDINAL`. But this type is specified only for `least` and `greatest` constructs. -", Remove(false)); +".TrimStart(), Remove(false)); Add(ErrorId.p_constructors_have_no_out_parameters, @" @@ -509,7 +524,7 @@ a reference to the newly constructed object (as in `new C(42)`). There is no syntax to receive out-parameter values of a constructor and they may not be declared. (This is similar to constructors in other programming languages, like Java.) -", Remove(true, "remove out parameters")); +".TrimStart(), Remove(true, "remove out parameters")); Add(ErrorId.p_reads_star_must_be_alone, @" @@ -518,7 +533,7 @@ A reads clause lists the objects whose fields the function is allowed to read (o So it does not make sense to list `*` along with something more specific. If you mean that the function should be able to read anything, just list `*`. Otherwise, omit the `*` and list expressions containing all the objects that are read. -", range => new List { +".TrimStart(), range => new List { OneAction("remove *", IncludeComma(range), "", true) }); @@ -528,54 +543,54 @@ So it does not make sense to list `*` along with something more specific. with just an identifier and a type, separated by a colon. No initializing value may be given. If a default value is needed, assign the out-parameter that value as a first statement in the body of the method. -", Remove(true, "remove initializer")); // TODO - could be improved by removing leading whitespace +".TrimStart(), Remove(true, "remove initializer")); // TODO - could be improved by removing leading whitespace Add(ErrorId.p_set_only_one_type_parameter, @" A `set` type has one type parameter, namely the type of the elements of the set. The error message states that the parser sees some number of type parameters different than one. The type parameters are listed in a comma-separated list between `<` and `>`, after the type name. -"); // TODO - code action: keep only first parameter, and for susequent errors +".TrimStart()); // TODO - code action: keep only first parameter, and for susequent errors Add(ErrorId.p_iset_only_one_type_parameter, @" A `iset` type has one type parameter, namely the type of the elements of the set. The error message states that the parser sees some number of type parameters different than one. The type parameters are listed in a comma-separated list between `<` and `>`, after the type name. -"); +".TrimStart()); Add(ErrorId.p_multiset_only_one_type_parameter, @" A `multiset` type has one type parameter, namely the type of the elements of the multiset. The error message states that the parser sees some number of type parameters different than one. The type parameters are listed in a comma-separated list between `<` and `>`, after the type name. -"); +".TrimStart()); Add(ErrorId.p_seq_only_one_type_parameter, @" A `seq` type has one type parameter, namely the type of the elements of the sequence. The error message states that the parser sees some number of type parameters different than one. The type parameters are listed in a comma-separated list between `<` and `>`, after the type name. -"); +".TrimStart()); Add(ErrorId.p_map_needs_two_type_parameters, @" A `map` type has two type parameters: the type of the keys and the type of the values. The error message states that the parser sees some number of type parameters different than two. -"); +".TrimStart()); Add(ErrorId.p_imap_needs_two_type_parameters, @" A `imap` type has two type parameters: the type of the keys and the type of the values. The error message states that the parser sees some number of type parameters different than two. -"); +".TrimStart()); Add(ErrorId.p_no_ghost_arrow_type_arguments, @" [Arrow types](../DafnyRef/DafnyRef#sec-arrow-types) are the types of functions in Dafny. They are designated with the arrow syntax, as shown in the example, except that the types used cannot be declared as ghost. -", Remove(true, "remove ghost keyword")); +".TrimStart(), Remove(true, "remove ghost keyword")); Add(ErrorId.p_no_empty_type_parameter_list, @" @@ -585,7 +600,7 @@ there is no list and no angle brackets either. However, this particular error message is not reachable in the current parser. If the message is seen, please report the code that caused it so that the bug or documentation can be corrected. -"); +".TrimStart()); Add(ErrorId.p_formal_ktype_only_in_least_and_greatest_predicates, @" @@ -595,7 +610,7 @@ The type of that special formal is given in square brackets between the predicate name and the opening parenthesis of the formals. The type may be either `nat` or `ORDINAL`. This special formal is not permitted in a regular (non-extreme) predicate. -", Remove(true)); // TODO - would like to have option to add 'least' or 'greatest', but that takes a different token location +".TrimStart(), Remove(true)); // TODO - would like to have option to add 'least' or 'greatest', but that takes a different token location Add(ErrorId.p_no_by_method_in_twostate, @" @@ -604,13 +619,13 @@ Such functions use values from two different program (heap) states, which is not something that can be implemented (at least with any degree of good performance) in conventional programming languages. Because there is no feasible compiled verion of a two-state function, it cannot have a `by method` block (which is always compiled). -", Remove(false, "remove by method block")); +".TrimStart(), Remove(false, "remove by method block")); Add(ErrorId.p_no_by_method_in_extreme_predicate, @" Least and greatest predicates are always ghost and do not have a compiled representation, so it makes no sense to have a `by method` alternative implementation. -", Remove(false, "remove by method block")); +".TrimStart(), Remove(false, "remove by method block")); Add(ErrorId.p_no_by_method_for_ghost_function, @" @@ -619,13 +634,13 @@ so it makes no sense to have a `by method` alternative implementation. Uses of the function are verified using the function body, but the method body is used when the function is compiled. Thus the function part is always implicitly `ghost` and may not be explicitly declared `ghost`. -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_twostate_and_extreme_are_always_ghost, @" The `twostate`, `least`, and `greatest` functions and predicates are always ghost and cannot be compiled, so they cannot be declared as a `function method` (v3 only). -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_old_ghost_syntax, @" @@ -633,7 +648,7 @@ This error only occurs when the `experimentalPredicateAlwaysGhost` option is cho It indicates that `predicates` are always ghost and cannot be declared with the (Dafny 3) syntax `predicate method`. - If you intend to predicate to be ghost, remove `method`. - If you intend the predicate to be non-ghost, you either cannot use `experimentalPredicateAlwaysGhost` or you should use `function` with a `bool` return type instead of `predicate` -", Remove(true)); +".TrimStart(), Remove(true)); Add(ErrorId.p_deprecating_predicate_method, @" @@ -641,7 +656,7 @@ This error only occurs when the `experimentalPredicateAlwaysGhost` option is cho longer accepted. Use `function` for compiled, non-ghost functions and `ghost function` for non-compiled, ghost functions, and similarly for predicates. See [the documentation here](https://dafny.org/latest/DafnyRef/DafnyRef#sec-function-syntax). -", range => new List { +".TrimStart(), range => new List { OneAction("remove 'method'", range, "predicate", false), }); @@ -651,7 +666,7 @@ See [the documentation here](https://dafny.org/latest/DafnyRef/DafnyRef#sec-func longer accepted. Use `function` for compiled, non-ghost functions and `ghost function` for non-compiled, ghost functions, and similarly for predicates. See [the documentation here](https://dafny.org/latest/DafnyRef/DafnyRef#sec-function-syntax). -", range => new List { +".TrimStart(), range => new List { OneAction("remove 'method'", range, "function", false), }); @@ -666,7 +681,7 @@ See [the documentation here](https://dafny.org/latest/DafnyRef/DafnyRef#sec-func and there is no longer any declaration of the form `function method`, and similarly for predicates. See [the documentation here](../DafnyRef/DafnyRef#sec-function-syntax). -", range => new List { +".TrimStart(), range => new List { OneAction("remove 'method'", range, "function", false), }); @@ -681,7 +696,7 @@ See [the documentation here](../DafnyRef/DafnyRef#sec-function-syntax). and there is no longer any declaration of the form `function method`, and similarly for predicates. See [the documentation here](../DafnyRef/DafnyRef#sec-function-syntax). -", range => new List { +".TrimStart(), range => new List { OneAction("remove 'method'", range, "predicate", false), }); @@ -689,7 +704,7 @@ See [the documentation here](../DafnyRef/DafnyRef#sec-function-syntax). @" This error occurs only when using `migration3to4`. With this option, ghost functions are declared using `ghost function` and compiled functions using `function method`. Change `function` in the declaration to one of these. -", range => new List { +".TrimStart(), range => new List { OneAction("add 'ghost'", range, "ghost function", false), OneAction("add 'method'", range, "function method", false), }); @@ -697,7 +712,7 @@ See [the documentation here](../DafnyRef/DafnyRef#sec-function-syntax). Add(ErrorId.p_no_ghost_formal, @" A ghost predicate or function effectively has all ghost formal parameters, so they cannot be declared ghost in addition. -", range => range.PrintOriginal() != "ghost" ? +".TrimStart(), range => range.PrintOriginal() != "ghost" ? new List { } : new List { OneAction("remove 'ghost'", range, "", true) } ); @@ -706,14 +721,14 @@ See [the documentation here](../DafnyRef/DafnyRef#sec-function-syntax). @" Least and greatest predicates are not checked for termination. In fact, unbounded recursion is part of being coinductive. Hence `decreases` clauses are inappropriate and not allowed. -", Remove(false, "remove decreases clause")); +".TrimStart(), Remove(false, "remove decreases clause")); Add(ErrorId.p_predicate_return_type_must_be_bool, @" A predicate is a function that returns `bool`. The return type here is something else. If you mean to have a non-`bool` return type, use `function` instead of `predicate`. -", range => new List { - OneAction("remove type", new RangeToken(range.StartToken.Prev, range.EndToken), "", true), +".TrimStart(), range => new List { + OneAction("remove type", new SourceOrigin(range.StartToken.Prev, range.EndToken), "", true), OneAction("replace type with 'bool'", range, "bool", true) } ); @@ -722,8 +737,8 @@ See [the documentation here](../DafnyRef/DafnyRef#sec-function-syntax). A `predicate` is simply a `function` that returns a `bool` value. Accordingly, the type is (required to be) omitted, unless the result is being named. So `predicate p(): (res: bool) { true }` is permitted. -", range => new List { - OneAction("remove type", new RangeToken(range.StartToken.Prev, range.EndToken), "", true), +".TrimStart(), range => new List { + OneAction("remove type", new SourceOrigin(range.StartToken.Prev, range.EndToken), "", true), }); Add(ErrorId.p_no_wild_expression, @@ -731,14 +746,14 @@ See [the documentation here](../DafnyRef/DafnyRef#sec-function-syntax). A method or loop with a `decreases *` clause is not checked for termination. This is only permitted for non-ghost methods and loops. Insert an actual decreases expression. -"); +".TrimStart()); Add(ErrorId.p_no_wild_frame_expression, @" A `reads *` clause means the reads clause allows the functions it specifies to read anything. Such a clause is not allowed in an iterator specification. Insert a specific reads expression. -"); +".TrimStart()); Add(ErrorId.p_invalid_colon, @" @@ -747,7 +762,7 @@ This is not a legal start to a statement. Most commonly either * a `var` or `const` keyword is missing, and the `x:` is the beginning of a declaration, or * a `label` keyword is missing and the identifier is the label for the statement that follows. (The second error is somewhat common because in C/C++ and Java, there is no keyword introducing a label, just the identifier and the colon.) -", range => new List { +".TrimStart(), range => new List { OneAction("insert 'label'", range, "label " + range.PrintOriginal(), false), OneAction("insert 'var'", range, "var " + range.PrintOriginal(), false), OneAction("insert 'const'", range, "const " + range.PrintOriginal(), false), @@ -760,9 +775,9 @@ This is not a legal start to a statement. Most commonly either but multi-dimensional arrays cannot. The alternatives are to initialize the array in a loop after it is allocated, or to initialize with a function, as in `var a:= new int[2,2]((i: int, j: int)=>i+j)`. -"); +".TrimStart()); - ActionSignature sharedLambda = delegate (RangeToken range) { + ActionSignature sharedLambda = delegate (SourceOrigin range) { return new List { OneAction("replace with ':='", range, ":=", false), OneAction("replace with ':-", range, ":-", false), @@ -774,7 +789,7 @@ but multi-dimensional arrays cannot. The alternatives are to initialize the arra @" Local variables are initialized with `:=` (and sometimes with `:-` or `:|`), but not with `=`. In Dafny `=` is used only in type definitions. -", sharedLambda); +".TrimStart(), sharedLambda); Add(ErrorId.p_no_patterns_and_such_that, @" @@ -782,12 +797,12 @@ but multi-dimensional arrays cannot. The alternatives are to initialize the arra some non-deterministic values that satisfy the predicate on the right-hand-side. However, Dafny only allows a list of simple variables on the left, not datatype deconstructor patterns, as seen here. -"); +".TrimStart()); Add(ErrorId.p_no_modifies_on_refining_loops, @" _Refining statements, including loops, are deprecated._ -"); +".TrimStart()); Add(ErrorId.p_to_or_downto, @" @@ -810,7 +825,7 @@ method m() { } ``` is legal, but not at all recommended. -", range => new List { +".TrimStart(), range => new List { OneAction("replace with 'to'", range, "to", false), OneAction("replace with 'downto'", range, "downto", false), }); @@ -824,21 +839,21 @@ In loops it assures termination of the loop iteration. `decreases *` means to skip the termination check. So it does not make sense to both list a metric and to list '*'. Use one or the other. -"); // TODO - somewhere else where we remove the * or replace the whole works? +".TrimStart()); // TODO - somewhere else where we remove the * or replace the whole works? Add(ErrorId.p_assert_needs_by_or_semicolon, @" Assert statements, like all statements, end in either a semicolon or a block. Most assert statements end in semicolons, but an assert-by statement has the form `assert expr by { ... }` where the by-block contains statements such as lemma calls that assist in proving the validity of the asserted expression. -", InsertAfter(";", "insert semicolon")); +".TrimStart(), InsertAfter(";", "insert semicolon")); Add(ErrorId.p_forall_with_ensures_must_have_body, @" A forall statement without a body is like an assume statement: the ensures clause is assumed in the following code. Assumptions like that are a risk to soundness because there is no check that the assumption is true. Thus in a context in which open assumptions are not allowed, body-less forall statements are also not allowed. -"); +".TrimStart()); Add(ErrorId.p_calc_operator_must_be_transitive, @" @@ -851,7 +866,7 @@ But the sequence of operators must obey certain patterns similar to chaining exp in the body of the calc statement. But the operator has to be transitive: `!=` is not allowed; `==`, `<`, `<=`, '>' and '>=' are allowed. -", range => new List { +".TrimStart(), range => new List { OneAction("replace with '=='", range, "==", false), OneAction("replace with '<'", range, "<", false), OneAction("replace with '<='", range, "<=", false), @@ -869,7 +884,7 @@ But the sequence of operators must obey certain patterns similar to chaining exp In particular, this error message is complaining that it sees an unacceptable operator. In this case, the reason is that the sequence may contain only one `!=` operator; another reason causing this message would be a combination of `<` and `>` operators. -"); +".TrimStart()); Add(ErrorId.p_calc_dangling_operator, @" @@ -881,14 +896,14 @@ But the sequence must begin and end with (semicolon-terminated) expressions. This error message is complaining that it sees an operator ending the sequence. This may be because there is no following expression or that the parser does not recognize the material after the last operator as a legal ending expression. -", Remove(false)); +".TrimStart(), Remove(false)); Add(ErrorId.p_no_side_effects_in_expressions, @" Dafny expressions may not have side-effects. This prohibits both assignments to local variables and any changes to the heap. Thus method and constructor calls may not occur in expressions. This check is syntactic, so even methods that do not modify anything are not permitted in expressions. -"); +".TrimStart()); Add(ErrorId.p_ambiguous_implies, @" @@ -900,7 +915,7 @@ changes to the heap. Thus method and constructor calls may not occur in expressi `p <== q <== r` is `(p <== q) <== r`. See [this section](../DafnyRef/DafnyRef#sec-implication-and-reverse-implication) for more information. -"); // TODO - would be nice to have code actions for the alternatives, but that requires passing multiple ranges +".TrimStart()); // TODO - would be nice to have code actions for the alternatives, but that requires passing multiple ranges Add(ErrorId.p_ambiguous_implies_2, @" The `==>` and `<==` operators have the same precedence but do not associate with each other. @@ -911,14 +926,14 @@ changes to the heap. Thus method and constructor calls may not occur in expressi `p <== q <== r` is `(p <== q) <== r`. See [this section](../DafnyRef/DafnyRef#sec-implication-and-reverse-implication) for more information. -"); // TODO - would be nice to have code actions for the alternatives, but that requires passing multiple ranges +".TrimStart()); // TODO - would be nice to have code actions for the alternatives, but that requires passing multiple ranges Add(ErrorId.p_ambiguous_and_or, @" The `&&` and `||` operators have the same precedence but do not associate with each other. You must use parentheses to show how they are grouped. Write `p && q || r` as either `(p && q) || r` or `p && (q || r)`. -");// TODO - would be nice to have code actions for the alternatives, but that requires passing multiple ranges +".TrimStart());// TODO - would be nice to have code actions for the alternatives, but that requires passing multiple ranges Add(ErrorId.p_invalid_equal_chaining, @" @@ -928,7 +943,7 @@ But there are limitations on which operators can be in one chain together. In particular, the relational operators `in` and `!in` may not be part of a chain. Use parentheses as necessary to group the operations. -"); +".TrimStart()); Add(ErrorId.p_invalid_notequal_chaining, @" @@ -937,7 +952,7 @@ [Chained operations](../DafnyRef/DafnyRef#sec-basic-types) But there are limitations on which operators can be in one chain together. In particular for this error message, one cannot have chains that include more than one `!=` operator. -"); +".TrimStart()); Add(ErrorId.p_invalid_operator_in_chain, @" @@ -946,7 +961,7 @@ [Chained operations](../DafnyRef/DafnyRef#sec-basic-types) But there are limitations on which operators can be in one chain together. In particular for this error message, the designated operator is not permitted to extend the existing chain. -"); +".TrimStart()); Add(ErrorId.p_invalid_descending_chaining, @" @@ -956,7 +971,7 @@ But there are limitations on which operators can be in one chain together. In particular for this error message, one cannot have chains that include both less-than operations (either `<` or `<=`) and greater-than operations (either `>` or `>=`). -"); +".TrimStart()); Add(ErrorId.p_invalid_ascending_chaining, @" @@ -966,7 +981,7 @@ But there are limitations on which operators can be in one chain together. In particular for this error message, one cannot have chains that include both less-than operations (either `<` or `<=`) and greater-than operations (either `>` or `>=`). -"); +".TrimStart()); Add(ErrorId.p_invalid_disjoint_chaining, @" @@ -980,13 +995,13 @@ But there are limitations on which operators can be in one chain together. As described [here](../DafnyRef/DafnyRef#sec-collection-types), `a !! b !! c !! d` means that `a`, `b`, `c`, and `d` are all mutually disjoint (which is a different rewriting of the chain than for other operations). -"); +".TrimStart()); Add(ErrorId.p_operator_does_not_chain, @" The operators `in` and `!in` are relational operators, but they may not occur in a chain. Use parentheses if necessary. Such expressions are usually not type-correct in any case. -"); +".TrimStart()); Add(ErrorId.p_bang_not_a_relational_op, @" @@ -994,7 +1009,7 @@ As described [here](../DafnyRef/DafnyRef#sec-collection-types), (one of `==`, `!=`, `>`, `>=`, `<`, `<=`, `!!`, `in`, `!in`). But the parser saw just a `!` , which could be the beginning of `!=`, `!!`, or `!in`, but is not continued as such. So perhaps there is extraneous white space or something else entirely is intended. -", range => new List { +".TrimStart(), range => new List { OneAction("replace with `!=`", range, "!=", false), OneAction("replace with `!!`", range, "!!", false), }); @@ -1004,14 +1019,14 @@ As described [here](../DafnyRef/DafnyRef#sec-collection-types), The parser is expecting a relational expression, that is, two expressions separated by a relational operator (one of `==`, `!=`, `>`, `>=`, `<`, `<=`, `!!`, `in`, `!in`). But the parser saw two `!` separated by white space. This is possibly meant to be a `!!` operator, but it could also just be an illegal expression. -", Replace("!!")); +".TrimStart(), Replace("!!")); Add(ErrorId.p_ambiguous_bitop, @" The bit-operators `&`, `|`, and `^` have the same precedence but do not associate with each other. So if they are used within the same expression, parentheses have to be used to show how they are grouped. The example `5 | 6 & 7` should be written as either `(5 | 6) & 7` or `5 | (6 & 7)`. -"); +".TrimStart()); Add(ErrorId.p_invalid_char_literal, @" @@ -1025,13 +1040,13 @@ so this means a character literal can only contain a character that can be repre Unicode scalar value, but be aware that it may change the meaning of your program. More detail is given [here](../DafnyRef/DafnyRef#sec-character-constant-token) and [here](../DafnyRef/DafnyRef#sec-escaped-characters).; -"); +".TrimStart()); Add(ErrorId.p_no_parenthesized_binding, @" Bindings of the form `x := y` are used in map-display expressions, in which case they are enclosed in square brackets, not parentheses. `var c := ( 4 := 5 )` should be `var c := map[ 4 := 5 ]`. -", range => new List { +".TrimStart(), range => new List { OneAction("replace `( )` with `map[ ]`", range, "map[" + range.PrintOriginal()[1..^1] + "]", false), OneAction("replace `( )` with `imap[ ]`", range, "imap[" + range.PrintOriginal()[1..^1] + "]", false), }); @@ -1046,13 +1061,13 @@ A set/iset/multiset display expression can have two forms. In the current parser, however, this error message is unreachable, so if it appears please report the error. The tests that check for this error case are already known to be false by previous testing. -"); +".TrimStart()); Add(ErrorId.p_seq_display_has_one_type_argument, @" The built-in `seq` (sequence) type takes one type parameter, which in some situations is inferred. That type parameter is the type of the sequence elements. -"); // TODO - get rid of the extra parameters +".TrimStart()); // TODO - get rid of the extra parameters Add(ErrorId.p_map_comprehension_must_have_term_expression, @" @@ -1073,7 +1088,7 @@ because it is not clear what the left-hand expression should be. Incorrect text like `const s := map x, y | 0 <= x < y < 10 :: x*y` should be written as `const s := map x, y | 0 <= x < y < 10 :: f(x,y) := x*y` for some `f(x,y)` that gives a unique value for each pair of `x,y` permitted by the range expression (here `0 <= x < y < 10`). -"); +".TrimStart()); Add(ErrorId.p_no_patterns_in_let_such_that, @" @@ -1082,18 +1097,18 @@ The let-such-that expression initializes a variable to some value satisfying a g where `cc` would get some value `x` satisfying `x < 10`. For simplicity, however, Dafny requires the variables being initialized to be simple names, not patterns. -"); +".TrimStart()); Add(ErrorId.p_no_equal_in_let_initialization, @" Like local variables, let variables are initialized with `:=` (and sometimes with `:-` or `:|`), but not with `=`. In Dafny `=` is used only in type definitions. -", sharedLambda); +".TrimStart(), sharedLambda); Add(ErrorId.p_elephant_has_one_lhs, @" Within a function, the `:-` operator is limited to a most one left-hand-side and exactly one-right-hand-side. -", Remove(false, "remove extra LHSs")); +".TrimStart(), Remove(false, "remove extra LHSs")); Add(ErrorId.p_elephant_has_one_rhs, @" @@ -1104,7 +1119,7 @@ and in the context of a let-or-fail expression. In contrast to the let expression (`:=`), which allows multiple parallel initializations, the let-or-fail expression (`:-`) is implemented to only allow at most a single left-hand-side and exactly one right-hand-side. -", Remove(false, "remove extra RHSs")); +".TrimStart(), Remove(false, "remove extra RHSs")); Add(ErrorId.p_set_comprehension_needs_term_expression, @" @@ -1118,13 +1133,13 @@ A set comprehension (1) declares one or more variables, (2) possibly states some But if more than one variable is declared, then there is no natural implicit expression to fill in after the `::` if it is omitted, so some expression is required. The failing example above, for example, might use the expression `x * y`, as in `set x, y | 0 <= x < y < 10 :: x * y`, or any other expression over `x` and `y`. -", InsertAfter(" :: 0", "insert example expression")); +".TrimStart(), InsertAfter(" :: 0", "insert example expression")); Add(ErrorId.p_invalid_name_after_dot, @" This error message is not reachable in current Dafny. If it occurs, please report an internal bug (or obsolete documentation). -"); +".TrimStart()); Add(ErrorId.p_bad_number_format, @" @@ -1132,7 +1147,7 @@ This error can only result from an internal bug in the Dafny parser. The parser recognizes a legitimate sequence of digits (as an integer literal and then passes that string to a library routine to create a BigInteger or BigDecimal. Given the parser logic, that parsing should never fail. -"); +".TrimStart()); Add(ErrorId.p_bad_hex_number_format, @" @@ -1140,7 +1155,7 @@ This error can only result from an internal bug in the Dafny parser. The parser recognizes a legitimate sequence of hexdigits and then passes that string to a library routine to create a BigInteger. Given the parser logic, that parsing should never fail. -"); +".TrimStart()); Add(ErrorId.p_bad_decimal_number_format, @" @@ -1148,7 +1163,7 @@ This error can only result from an internal bug in the Dafny parser. The parser recognizes a legitimate Dafny decimal number and then passes that string to a library routine to create a BigDecimal. Given the parser logic, that parsing should never fail. -"); +".TrimStart()); Add(ErrorId.p_generic_syntax_error, @" @@ -1163,59 +1178,61 @@ The only advice we can give is to carefully scrutinize the location of the error to see what might be wrong with the text. If you think this is a common or confusing enough occurrence to warrant special error handling, please suggest the improvement, with this sample code, to the Dafny team. -"); +".TrimStart()); Add(ErrorId.p_deprecated_semicolon, @" Semicolons are required after statements and declarations in method bodies, but are deprecated after declarations within modules and types. -", Remove(true, "remove semicolon")); +".TrimStart(), Remove(true, "remove semicolon")); Add(ErrorId.p_file_has_no_code, @" The indicated file has no code. This can be because the file is empty, because some parse error left the top-level module with no well-formed declarations, or because a unclosed comment has commented-out the whole file. -"); +".TrimStart()); Add(ErrorId.p_internal_exception, @" This error indicates an internal crashing bug in Dafny. Please report it with as much of the source code that causes the problem as possible. -"); +".TrimStart()); Add(ErrorId.p_deprecated_inductive_predicate, @" The terms `least predicate` and `greatest predicate` are more descriptive of the relationship between them than was the old terminology. -"); +".TrimStart()); Add(ErrorId.p_deprecated_copredicate, @" The terms `least predicate` and `greatest predicate` are more descriptive of the relationship between them than was the old terminology. -"); +".TrimStart()); Add(ErrorId.p_deprecated_statement_refinement, @" Statement refinement has been deprecated. Refinement is restricted to changing declarations, not bodies of methods or functions. -"); +".TrimStart()); Add(ErrorId.p_deprecated_forall_with_no_bound_variables, @" -"); +".TrimStart()); Add(ErrorId.p_deprecated_modify_statement_with_block, @" -"); +".TrimStart()); Add(ErrorId.p_deprecated_opaque_as_identifier, @" Because of the value to proof success of using `opaque` declarations and `reveal`ing them in appropriate contexts, the word `opaque` is being converted to a reserved keyword, whereas it used to be a normal identifier. Please rename your use of opaque as an identifier to some other name. -"); +".TrimStart()); } + public static void RunStaticConstructor() { + } } diff --git a/Source/DafnyCore/AST/Grammar/ParserNonGeneratedPart.cs b/Source/DafnyCore/AST/Grammar/ParserNonGeneratedPart.cs new file mode 100644 index 00000000000..a2dee52d68f --- /dev/null +++ b/Source/DafnyCore/AST/Grammar/ParserNonGeneratedPart.cs @@ -0,0 +1,876 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.CommandLine; +using System.Diagnostics.Contracts; +using System.Threading; +using static Microsoft.Dafny.ParseErrors; + +namespace Microsoft.Dafny; + +public partial class Parser { + + public Parser(DafnyOptions options, Scanner/*!*/ scanner, Errors/*!*/ errors, CancellationToken cancellationToken) + : this(scanner, errors, cancellationToken) // the real work + { + // initialize readonly fields + dummyExpr = new LiteralExpr(Token.NoToken); + dummyRhs = new ExprRhs(dummyExpr); + dummyFrameExpr = new FrameExpression(dummyExpr.Origin, dummyExpr, null); + dummyStmt = new ReturnStmt(Token.NoToken, null); + var dummyBlockStmt = new BlockStmt(Token.NoToken, new List()); + dummyIfStmt = new IfStmt(Token.NoToken, false, null, dummyBlockStmt, null); + + theOptions = new DafnyOptions(options); + theModule = new FileModuleDefinition(scanner.FirstToken); + } + + public void MergeInto(ref Attributes attrsStack, ref Attributes attrsTarget) { + Attributes.MergeInto(ref attrsStack, ref attrsTarget); + } + + public Attributes Consume(ref Attributes attrs) { + return Attributes.Consume(ref attrs); + } + + bool IsReveal(IOrigin nextToken) => la.kind == _reveal || (la.kind == _hide && nextToken.kind is _star or _ident); + + bool IsIdentifier(int kind) { + return kind == _ident || kind == _least || kind == _greatest || kind == _older || kind == _opaque; + } + + bool IsQuantifierVariableDecl(QuantifiedVar previousVar) { + // Introducing per-quantified variable ranges creates some ambiguities in the grammar, + // since before that the range would terminate the quantifed domain. For example, the following statement: + // + // print set x | E, y; + // + // This would previously parse as two separate arguments to the print statement, but + // could now also be parsed as a single set comprehension argument with two quantified variables + // (and an invalid one since it would need an explicit ":: " section). + // + // Even worse: + // + // print set x | E, y <- C; + // + // This was a valid statement before as well, because "y <- C" would be parsed as the expression + // "y < (-C)". + // + // The /quantifierSyntax option is used to help migrate this otherwise breaking change: + // * /quantifierSyntax:3 keeps the old behaviour where a "| " always terminates the list of quantified variables. + // * /quantifierSyntax:4 instead attempts to parse additional quantified variables. + if (previousVar.Range != null && theOptions.QuantifierSyntax == QuantifierSyntaxOptions.Version3) { + return false; + } + + scanner.ResetPeek(); + IOrigin x = scanner.Peek(); + return la.kind == _comma && IsIdentifier(x.kind); + } + + // Checks for "<-", which has to be parsed as two separate tokens, + // but ensures no whitespace between them. + bool IsFromArrow() { + scanner.ResetPeek(); + IOrigin x = scanner.Peek(); + return la.kind == _openAngleBracket && x.kind == _minus + && la.line == x.line && la.col == x.col - 1; + } + + bool IsLabel(bool allowLabel) { + if (!allowLabel) { + return false; + } + scanner.ResetPeek(); + IOrigin x = scanner.Peek(); + return (IsIdentifier(la.kind) || la.kind == _digits) && x.kind == _colon; + } + + bool IsKeywordForFormal() { + scanner.ResetPeek(); + if (la.kind == _ghost || la.kind == _new || la.kind == _nameonly) { + return true; + } else if (la.kind == _older) { + // "older" is just a contextual keyword, so don't recognize it as a keyword if it must be an identifier + IOrigin x = scanner.Peek(); + return x.kind != _colon; + } + return false; + } + + bool IsBinding() { + scanner.ResetPeek(); + IOrigin x = scanner.Peek(); + return (IsIdentifier(la.kind) || la.kind == _digits) && x.kind == _gets; + } + + bool IsAlternative() { + IOrigin x = scanner.Peek(); + return (la.kind == _lbrace && x.kind == _case) + || la.kind == _case; + } + + bool IsParenIdentsColon() { + IOrigin x = la; + if (x.kind != _openparen) { + return false; + } + x = scanner.Peek(); + var oneOrMoreIdentifiers = false; + while (IsIdentifier(x.kind) || x.kind == _ghost) { /* ghost is illegal here, but checking for it allows better error messages and recovery */ + x = scanner.Peek(); + oneOrMoreIdentifiers = true; + } + return oneOrMoreIdentifiers && x.kind == _colon; + } + + bool IsGets() { + return la.kind == _gets; + } + + bool IsPeekVar() { + scanner.ResetPeek(); + IOrigin x = scanner.Peek(); + return x.kind == _var; + } + + // an existential guard starts with an identifier and is then followed by + // * a colon (if the first identifier is given an explicit type), + // * a comma (if there's a list of bound variables and the first one is not given an explicit type), + // * a start-attribute (if there's one bound variable and it is not given an explicit type and there are attributes), or + // * a bored smiley (if there's one bound variable and it is not given an explicit type). + bool IsBindingGuard() { + scanner.ResetPeek(); + if (IsIdentifier(la.kind)) { + Token x = scanner.Peek(); + if (x.kind == _colon || x.kind == _comma || x.kind == _boredSmiley || x.kind == _lbracecolon) { + return true; + } + } + return false; + } + + bool IsLoopSpec() { + return la.kind == _invariant || la.kind == _decreases || la.kind == _modifies; + } + + bool IsWitness() { + scanner.ResetPeek(); + if (la.kind == _witness) { + return true; + } else if (la.kind == _ghost) { + Token x = scanner.Peek(); + return x.kind == _witness; + } + return false; + } + + bool IsFunctionDecl() { + var kind = la.kind; + return IsFunctionDecl(kind); + } + + bool IsDecreasesTo() { + scanner.ResetPeek(); + if (la.kind is _decreases or _nonincreases) { + Token x = scanner.Peek(); + if (x.kind == _ident && x.val == "to") { + return true; + } + } + return false; + } + + private bool IsFunctionDecl(int kind) { + switch (kind) { + case _function: + case _predicate: + case _copredicate: + return true; + case _least: + case _greatest: + case _inductive: + return scanner.Peek().kind != _lemma; + case _twostate: + var x = scanner.Peek(); + return x.kind == _function || x.kind == _predicate; + default: + return false; + } + } + + bool IsParenStar() { + scanner.ResetPeek(); + Token x = scanner.Peek(); + return la.kind == _openparen && x.kind == _star; + } + + bool IsEquivOp() => IsEquivOp(la); + bool IsImpliesOp() => IsImpliesOp(la); + bool IsExpliesOp() => IsExpliesOp(la); + bool IsAndOp() => IsAndOp(la); + bool IsOrOp() => IsOrOp(la); + bool IsComma() { + return la.val == ","; + } + static bool IsEquivOp(IOrigin la) { + return la.val == "<==>"; + } + static bool IsImpliesOp(IOrigin la) { + return la.val == "==>"; + } + static bool IsExpliesOp(IOrigin la) { + return la.val == "<=="; + } + static bool IsAndOp(IOrigin la) { + return la.val == "&&"; + } + static bool IsOrOp(IOrigin la) { + return la.val == "||"; + } + bool IsBitwiseAndOp() { + return la.val == "&"; + } + bool IsBitwiseOrOp() { + return la.val == "|"; + } + bool IsBitwiseXorOp() { + return la.val == "^"; + } + bool IsBitwiseOp() { + return IsBitwiseAndOp() || IsBitwiseOrOp() || IsBitwiseXorOp(); + } + bool IsAsOrIs() { + return la.kind == _as || la.kind == _is; + } + bool IsRelOp() { + return la.val == "==" + || la.val == "<" + || la.val == ">" + || la.val == "<=" + || la.val == ">=" + || la.val == "!=" + || la.val == "in" + || la.kind == _notIn + || la.val == "!"; + } + bool IsShiftOp() { + if (la.kind == _openAngleBracket) { + } else if (la.kind == _closeAngleBracket) { + } else { + return false; + } + scanner.ResetPeek(); + var x = scanner.Peek(); + if (x.kind != la.kind) { + return false; + } + return x.pos == la.pos + 1; // return true only if the tokens are adjacent to each other + } + bool IsAddOp() { + return la.val == "+" || la.val == "-"; + } + bool IsMulOp() { + return la.kind == _star || la.val == "/" || la.val == "%"; + } + bool IsQSep() { + return la.kind == _doublecolon; + } + + bool IsNonFinalColon() { + return la.kind == _colon && scanner.Peek().kind != _rbracket; + } + + bool ExprIsMapDisplay() { + scanner.ResetPeek(); + return (la.kind == _map || la.kind == _imap) && scanner.Peek().kind == _lbracket; + } + + bool ExprIsSetDisplay() { + scanner.ResetPeek(); + if (la.kind == _lbrace) { + return true; + } + + int k = scanner.Peek().kind; + if (la.kind == _iset && k == _lbrace) { + return true; + } + + if (la.kind == _multiset) { + return true; + } + + return false; + } + + bool IsSuffix() { + return la.kind == _dot || la.kind == _lbracket || la.kind == _openparen; + } + + string UnwildIdent(IOrigin x, bool allowWildcardId) { + if (x.val.StartsWith("_")) { + if (allowWildcardId && x.val.Length == 1) { + return "_v" + anonymousIds++; + } else { + SemErr(ErrorId.p_no_leading_underscore, x, "cannot declare identifier beginning with underscore"); + } + } + return x.val; + } + + bool IsLambda(bool allowLambda) { + if (!allowLambda) { + return false; + } + scanner.ResetPeek(); + Token x; + // peek at what might be a signature of a lambda expression + if (IsIdentifier(la.kind)) { + // cool, that's the entire candidate signature + } else if (la.kind != _openparen) { + return false; // this is not a lambda expression + } else { + int identCount = 0; + x = scanner.Peek(); + while (x.kind != _closeparen) { + if (identCount != 0) { + if (x.kind != _comma) { + return false; // not the signature of a lambda + } + x = scanner.Peek(); + } + if (!IsIdentifier(x.kind)) { + return false; // not a lambda expression + } + identCount++; + x = scanner.Peek(); + if (x.kind == _colon) { + // a colon belongs only in a lamdba signature, so this must be a lambda (or something ill-formed) + return true; + } + } + } + // What we have seen so far could have been a lambda signature or could have been some + // other expression (in particular, an identifier, a parenthesized identifier, or a + // tuple all of whose subexpressions are identifiers). + // It is a lambda expression if what follows is something that must be a lambda. + x = scanner.Peek(); + return x.kind == _darrow || x.kind == _reads || x.kind == _requires; + } + + bool IsIdentParen() { + scanner.ResetPeek(); + Token x = scanner.Peek(); + return IsIdentifier(la.kind) && x.kind == _openparen; + } + + /* Used to disambiguate the LHS of a VarDeclStmt. If it looks like the start of a CasePattern, + * we consider it to be a VarDeclPattern. But if we are looking at a simple identifier, then we + * consider it to be a VarDeclStmt. + */ + bool IsPatternDecl() { + return IsIdentParen() || la.kind == _openparen; + } + + bool IsIdentColonOrBar() { + Token x = scanner.Peek(); + return IsIdentifier(la.kind) && (x.kind == _colon || x.kind == _verticalbar); + } + + bool SemiFollowsCall(bool allowLemma, Expression e) { + return allowLemma && la.kind == _semicolon && e is ApplySuffix; + } + + bool IsNotEndOfCase() { + return la.kind != _EOF && la.kind != _rbrace && la.kind != _case; + } + + /* The following is the largest lookahead there is. It needs to check if what follows + * can be nothing but "<" Type { "," Type } ">". + * If inExpressionContext == true, it also checks the token immediately after + * the ">" to help disambiguate some cases (see implementation comment). + */ + bool IsGenericInstantiation(bool inExpressionContext) { + scanner.ResetPeek(); + IOrigin pt = la; + if (!IsTypeList(ref pt)) { + return false; + } + if (!inExpressionContext) { + return true; + } + /* There are ambiguities in the parsing. For example: + * F( a < b , c > (d) ) + * can either be a unary function F whose argument is a function "a" with type arguments "" and + * parameter "d", or can be a binary function F with the two boolean arguments "a < b" and "c > (d)". + * To make the situation a little better, we (somewhat heuristically) look at the character that + * follows the ">". Note that if we, contrary to a user's intentions, pick "a" out as a function + * with a type instantiation, the user can disambiguate it by making sure the ">" sits inside some + * parentheses, like: + * F( a < b , (c > (d)) ) + */ + // In the following cases, we're sure we must have read a type instantiation that just ended an expression + if (IsEquivOp(pt) || IsImpliesOp(pt) || IsExpliesOp(pt) || IsAndOp(pt) || IsOrOp(pt)) { + return true; + } + switch (pt.kind) { + case _dot: // here, we're sure it must have been a type instantiation we saw, because an expression cannot begin with dot + case _openparen: // it was probably a type instantiation of a function/method + case _lbracket: // it is possible that it was a type instantiation + case _lbrace: // it was probably a type instantiation of a function/method + case _at: + // In the following cases, we're sure we must have read a type instantiation that just ended an expression + case _closeparen: + case _rbracket: + case _rbrace: + case _comma: + case _semicolon: + case _then: + case _else: + case _case: + case _eq: + case _neq: + case _as: + case _is: + case _darrow: + case _by: + case _in: + case _openAngleBracket: + case _closeAngleBracket: + case _EOF: + // (specification clauses that can follow an expression) + case _decreases: + case _modifies: + case _reads: + case _requires: + case _ensures: + case _invariant: + case _witness: + // (top-level declarations that can follow an expression) + case _function: + case _predicate: + case _least: + case _greatest: + case _inductive: + case _twostate: + case _lemma: + case _copredicate: + case _ghost: + case _static: + case _import: + case _export: + case _class: + case _trait: + case _datatype: + case _codatatype: + case _var: + case _const: + case _newtype: + case _type: + case _iterator: + case _method: + case _colemma: + case _constructor: + return true; + default: + return false; + } + } + + // Returns true if the parser can parse an heap-referencing @-call + // The reason to do this is that expressions can be prefixed by @-Attributes, + // so the rule to distinguish them is that an @-call of the form name@label(args), + // the @ must be next to the name. Otherwise an attribute is parsed. + // Indeed 'name' could be the last expression of an ensures clause, and the attribute + // could belong to the next method declaration otherwise. + bool IsAtCall() { + IOrigin pt = la; + if (pt.val != "@") { + return false; + } + // If it's the beginning of the file, or the previous token is on a different line or separated by a space, it's not an At-call. Must be an attribute + var isFirstToken = pt.Prev == null; + var spaceExistsSincePreviousToken = + !isFirstToken && + (pt.Prev.line != pt.line || pt.Prev.col + pt.Prev.val.Length + pt.TrailingTrivia.Trim().Length < pt.col - pt.LeadingTrivia.Trim().Length); + if (isFirstToken || spaceExistsSincePreviousToken) { + return false; + } + + return true; + } + + /* Returns true if the next thing is of the form: + * "<" Type { "," Type } ">" + */ + bool IsTypeList(ref IOrigin pt) { + if (pt.kind != _openAngleBracket) { + return false; + } + pt = scanner.Peek(); + return IsTypeSequence(ref pt, _closeAngleBracket); + } + /* Returns true if the next thing is of the form: + * [ "ghost" ] Type { "," [ "ghost" ] Type } + * followed by an endBracketKind. + */ + bool IsTypeSequence(ref IOrigin pt, int endBracketKind) { + while (true) { + if (pt.kind == _ghost) { + pt = scanner.Peek(); + } + if (!IsType(ref pt)) { + return false; + } + if (pt.kind == endBracketKind) { + // end of type list + pt = scanner.Peek(); + return true; + } else if (pt.kind == _comma) { + // type list continues + pt = scanner.Peek(); + } else { + // not a type list + return false; + } + } + } + + bool IsType(ref IOrigin pt) { + if (!IsNonArrowType(ref pt)) { + return false; + } + + while (pt.kind == _sarrow || pt.kind == _qarrow || pt.kind == _larrow) { + pt = scanner.Peek(); + if (!IsNonArrowType(ref pt)) { + return false; + } + } + return true; + } + + bool IsNonArrowType(ref IOrigin pt) { + switch (pt.kind) { + case _bool: + case _char: + case _nat: + case _int: + case _real: + case _ORDINAL: + case _string: + case _object_q: + case _object: + pt = scanner.Peek(); + return true; + case _arrayToken: + case _bvToken: + case _set: + case _iset: + case _multiset: + case _seq: + case _map: + case _imap: + pt = scanner.Peek(); + return pt.kind != _openAngleBracket || IsTypeList(ref pt); + case _ident: + case _least: + case _greatest: + while (true) { + // invariant: next token is an identifier (_ident, _least, or _greatest) + pt = scanner.Peek(); + if (pt.kind == _openAngleBracket && !IsTypeList(ref pt)) { + return false; + } + if (pt.kind != _dot) { + // end of the type + return true; + } + pt = scanner.Peek(); // get the _dot + if (!IsIdentifier(pt.kind)) { + return false; + } + } + case _openparen: + pt = scanner.Peek(); + if (pt.kind == _closeparen) { + // end of type list + pt = scanner.Peek(); + return true; + } + return IsTypeSequence(ref pt, _closeparen); + default: + return false; + } + } + + + void ConvertKeywordTokenToIdent() { + var oldKind = la.kind; + la.kind = _ident; + + // call CheckLiteral with la + var origT = t; + t = la; + scanner.CheckLiteral(); + t = origT; + + if (la.kind != _ident) { + // it has been changed by CheckLiteral, which means it was a keyword + la.kind = _ident; // convert it to an ident + } else { + // la was something other than a keyword + la.kind = oldKind; + } + } + + int StringToInt(string s, int defaultValue, string errString, IOrigin tok) { + Contract.Requires(s != null); + Contract.Requires(errString != null); + try { + if (s != "") { + defaultValue = int.Parse(s); + } + } catch (System.OverflowException) { + SemErr(errString.Contains("array") ? ErrorId.p_array_dimension_too_large + : ErrorId.p_bitvector_too_large, + tok, string.Format("sorry, {0} ({1}) are not supported", errString, s)); + } + return defaultValue; + } + + readonly Expression/*!*/ dummyExpr; + readonly AssignmentRhs/*!*/ dummyRhs; + readonly FrameExpression/*!*/ dummyFrameExpr; + readonly Statement/*!*/ dummyStmt; + readonly Statement/*!*/ dummyIfStmt; + public readonly FileModuleDefinition theModule; + public readonly List> SystemModuleModifiers = new(); + DafnyOptions theOptions; + int anonymousIds = 0; + + /// + /// Holds the modifiers and attributes given for a declaration + /// + /// Not all modifiers are applicable to all kinds of declarations. + /// Errors are given when a modify does not apply. + /// We also record the tokens for the specified modifiers so that + /// they can be used in error messages. + /// + class DeclModifierData { + public bool IsReplaceable; + public IOrigin ReplaceableToken; + public bool IsAbstract; + public IOrigin AbstractToken; + public bool IsGhost; + public IOrigin GhostToken; + public bool IsStatic; + public IOrigin StaticToken; + public bool IsOpaque; + public IOrigin OpaqueToken; + public Token FirstTokenExceptAttributes; + public Attributes Attributes = null; + + public Token FirstToken { + get { + Token result = FirstTokenExceptAttributes; + foreach (var attr in Attributes.AsEnumerable()) { + if (result == null || result.pos > attr.Origin.pos) { + result = attr.StartToken; + } + } + + return result; + } + } + } + + private ModuleKindEnum GetModuleKind(DeclModifierData mods) { + if (mods.IsReplaceable && mods.IsAbstract) { + SemErr(null, mods.ReplaceableToken, "Can't be both replaceable and abstract"); + } + + if (mods.IsReplaceable) { + return ModuleKindEnum.Replaceable; + } + if (mods.IsAbstract) { + return ModuleKindEnum.Abstract; + } + + return ModuleKindEnum.Concrete; + } + + /// + /// Before literals that end a block, we usually add CheckNoAttributes to avoid any non-attached or dangling attributes + /// + public void CheckNoAttributes(ref Attributes attrs) { + if (attrs != null) { + SemErr(ErrorId.p_extra_attributes, attrs.Origin, "Attribute not expected here"); + attrs = null; + } + } + + // Check that token has not been set, then set it. + public void CheckAndSetToken(ref IOrigin token) { + if (token != null) { + SemErr(ErrorId.p_duplicate_modifier, t, "Duplicate declaration modifier: " + t.val); + } + token = t; + } + + // Check that token has not been set, then set it, but just ignores if it was set already + public void CheckAndSetTokenOnce(ref Token token) { + if (token == null) { + token = t; + } + } + + /// + // A flags type used to tell what declaration modifiers are allowed for a declaration. + /// + [Flags] + enum AllowedDeclModifiers { + None = 0, + Abstract = 1, + Ghost = 2, + + // Means ghost not allowed because already implicitly ghost. + AlreadyGhost = 4, + Static = 8, + Opaque = 16, + Replaceable = 32 + }; + + bool CheckAttribute(Errors errors, IOrigin attr, SourceOrigin range) { + // attr is the identifier of the Attribute + // range is from opening brace to closing brace + if (attr.val == "ignore") { + errors.Warning(ErrorId.p_deprecated_attribute, + range, + $"attribute :{attr.val} is deprecated"); + return false; + } + return true; + } + + bool IsAssumeTypeKeyword(IOrigin la) { + return la.kind == _assume || la.kind == _assert || la.kind == _expect; + } + + Expression ProcessTupleArgs(List args, IOrigin lp) { + if (args.Count == 1 && !args[0].IsGhost) { + if (args[0].FormalParameterName != null) { + SemErr(ErrorId.p_no_parenthesized_binding, lp, "binding not allowed in parenthesized expression"); + } + return args[0].Actual; + } else { + // Compute the actual position of ghost arguments + var ghostness = new bool[args.Count]; + for (var i = 0; i < args.Count; i++) { + ghostness[i] = false; + } + for (var i = 0; i < args.Count; i++) { + var arg = args[i]; + if (arg.IsGhost) { + if (arg.FormalParameterName == null) { + ghostness[i] = true; + } else { + var success = int.TryParse(arg.FormalParameterName.val, out var index); + if (success && 0 <= index && index < args.Count) { + ghostness[index] = true; + } + } + } + } + var argumentGhostness = ghostness.ToList(); + // make sure the corresponding tuple type exists + SystemModuleModifiers.Add(b => b.TupleType(lp, args.Count, true, argumentGhostness)); + return new DatatypeValue(lp, SystemModuleManager.TupleTypeName(argumentGhostness), SystemModuleManager.TupleTypeCtorName(args.Count), args); + } + } + + + public void ApplyOptionsFromAttributes(Attributes attrs) { + var overrides = attrs.AsEnumerable().Where(a => a.Name == "options" || a is UserSuppliedAtAttribute { UserSuppliedName: "Options" }) + .Reverse().Select(a => + (token: a.Origin, + options: UserSuppliedAtAttribute.GetUserSuppliedArguments(a).Select(arg => { + if (arg is not LiteralExpr { Value: string optStr }) { + SemErr(ErrorId.p_literal_string_required, arg.Origin, "argument to :options attribute must be a literal string"); + return null; + } + return optStr; + }).Where(opt => opt != null).ToArray())) + .Where(opts => opts.options.Any()); + + if (overrides.Any()) { + var options = new DafnyAttributeOptions(theOptions, errors); + foreach (var (token, opts) in overrides) { + + var newOptionsCommand = new RootCommand(); + newOptionsCommand.AddOption(CommonOptionBag.QuantifierSyntax); + newOptionsCommand.AddOption(Function.FunctionSyntaxOption); + var result = newOptionsCommand.Parse(string.Join(' ', opts)); + + if (!result.Errors.Any()) { + foreach (var option in newOptionsCommand.Options) { + var value = result.GetValueForOption(option); + options.Options.OptionArguments[option] = value; + options.ApplyBinding(option); + } + continue; + } + + options.Token = token; + options.Parse(opts); + } + theOptions = options; + } + } + + /// + /// Check the declaration modifiers against those that are allowed. + /// + /// The 'allowed' parameter specifies which declaration modifiers are allowed. + /// The 'declCaption' parameter should be a string describing the kind of declaration. + /// It is used in error messages. + /// Any declaration modifiers that are present but not allowed are cleared. + /// + void CheckDeclModifiers(ref DeclModifierData dmod, string declCaption, AllowedDeclModifiers allowed) { + declCaption = (declCaption.StartsWith("i") || declCaption.StartsWith("o") ? "an " : "a ") + declCaption; + if (dmod.IsAbstract && ((allowed & AllowedDeclModifiers.Abstract) == 0)) { + SemErr(ErrorId.p_abstract_not_allowed, dmod.AbstractToken, $"{declCaption} cannot be declared 'abstract'"); + dmod.IsAbstract = false; + } + if (dmod.IsReplaceable && ((allowed & AllowedDeclModifiers.Replaceable) == 0)) { + SemErr(ErrorId.p_abstract_not_allowed, dmod.ReplaceableToken, $"{declCaption} cannot be declared 'replaceable'"); + dmod.IsReplaceable = false; + } + if (dmod.IsGhost) { + if ((allowed & AllowedDeclModifiers.AlreadyGhost) != 0) { + if (declCaption.Contains("-by-method")) { + SemErr(ErrorId.p_no_ghost_for_by_method, dmod.GhostToken, + $"{declCaption} has a ghost function body and a non-ghost method body; {declCaption} declaration does not use the 'ghost' keyword."); + } else if (declCaption == "a function" || declCaption == "a predicate") { + SemErr(ErrorId.p_ghost_forbidden_default_3, dmod.GhostToken, $"{declCaption} cannot be declared 'ghost' (it is 'ghost' by default when using --function-syntax:3)"); + } else { + SemErr(ErrorId.p_ghost_forbidden_default, dmod.GhostToken, $"{declCaption} cannot be declared 'ghost' (it is 'ghost' by default)"); + } + dmod.IsGhost = false; + } else if ((allowed & AllowedDeclModifiers.Ghost) == 0) { + SemErr(ErrorId.p_ghost_forbidden, dmod.GhostToken, $"{declCaption} cannot be declared 'ghost'"); + dmod.IsGhost = false; + } + } + if (dmod.IsStatic && ((allowed & AllowedDeclModifiers.Static) == 0)) { + SemErr(ErrorId.p_no_static, dmod.StaticToken, $"{declCaption} cannot be declared 'static'"); + dmod.IsStatic = false; + } + if (dmod.IsOpaque && ((allowed & AllowedDeclModifiers.Opaque) == 0)) { + SemErr(ErrorId.p_no_opaque, dmod.OpaqueToken, $"{declCaption} cannot be declared 'opaque'"); + dmod.IsOpaque = false; + } + } + +} diff --git a/Source/DafnyCore/AST/Grammar/Printer.cs b/Source/DafnyCore/AST/Grammar/Printer.cs deleted file mode 100644 index 98c5f190bf0..00000000000 --- a/Source/DafnyCore/AST/Grammar/Printer.cs +++ /dev/null @@ -1,3105 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// Copyright by the contributors to the Dafny Project -// SPDX-License-Identifier: MIT -// -//----------------------------------------------------------------------------- - -using System; -using System.IO; -using System.Collections.Generic; -using System.CommandLine; -using System.Diagnostics.Contracts; -using System.Numerics; -using System.Linq; -using DafnyCore; -using JetBrains.Annotations; -using Bpl = Microsoft.Boogie; - -namespace Microsoft.Dafny { - - public enum PrintModes { - Everything, - Serialization, // Serializing the program to a file for lossless loading later - NoIncludes, - NoGhost - } - - - public record PrintFlags(bool UseOriginalDafnyNames = false); - - public class Printer { - private DafnyOptions options; - static Printer() { - DafnyOptions.RegisterLegacyBinding(PrintMode, (options, value) => { - options.PrintMode = value; - }); - - DooFile.RegisterNoChecksNeeded(new Option[] { - PrintMode - }); - } - - public static readonly Option PrintMode = new("--print-mode", () => PrintModes.Everything, @" -Everything - Print everything listed below. -DllEmbed - print the source that will be included in a compiled dll. -NoIncludes - disable printing of {:verify false} methods - incorporated via the include mechanism, as well as datatypes and - fields included from other files. -NoGhost - disable printing of functions, ghost methods, and proof - statements in implementation methods. It also disables anything - NoIncludes disables.".TrimStart()) { - IsHidden = true - }; - - TextWriter wr; - PrintModes printMode; - bool afterResolver; - bool printingExportSet = false; - bool printingDesugared = false; - private readonly PrintFlags printFlags; - - [ContractInvariantMethod] - void ObjectInvariant() { - Contract.Invariant(wr != null); - } - - public Printer(TextWriter wr, DafnyOptions options, PrintModes printMode = PrintModes.Everything, [CanBeNull] PrintFlags printFlags = null) { - Contract.Requires(wr != null); - this.wr = wr; - this.options = options; - this.printMode = printMode; - this.printFlags = printFlags ?? new PrintFlags(); - } - - public static string ExprToString(DafnyOptions options, Expression expr, [CanBeNull] PrintFlags printFlags = null) { - Contract.Requires(expr != null); - using var wr = new StringWriter(); - var pr = new Printer(wr, options, printFlags: printFlags); - pr.PrintExpression(expr, false); - return wr.ToString(); - } - - public static string GuardToString(DafnyOptions options, bool isBindingGuard, Expression expr) { - Contract.Requires(!isBindingGuard || (expr is ExistsExpr && ((ExistsExpr)expr).Range == null)); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr, options); - pr.PrintGuard(isBindingGuard, expr); - return wr.ToString(); - } - } - - public static string ExtendedExprToString(DafnyOptions options, Expression expr) { - Contract.Requires(expr != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr, options); - pr.PrintExtendedExpr(expr, 0, true, false); - return wr.ToString(); - } - } - - public static string FrameExprListToString(DafnyOptions options, List fexprs) { - Contract.Requires(fexprs != null); - using var wr = new StringWriter(); - var pr = new Printer(wr, options); - pr.PrintFrameExpressionList(fexprs); - return wr.ToString(); - } - - public static string StatementToString(DafnyOptions options, Statement stmt) { - Contract.Requires(stmt != null); - using var wr = new StringWriter(); - var pr = new Printer(wr, options); - pr.PrintStatement(stmt, 0); - return ToStringWithoutNewline(wr); - } - - public static string IteratorClassToString(DafnyOptions options, IteratorDecl iter) { - Contract.Requires(iter != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr, options); - pr.PrintIteratorClass(iter, 0, null); - return ToStringWithoutNewline(wr); - } - } - - public static string IteratorSignatureToString(DafnyOptions options, IteratorDecl iter) { - Contract.Requires(iter != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr, options); - pr.PrintIteratorSignature(iter, 0); - return ToStringWithoutNewline(wr); - } - } - - public static string FieldToString(DafnyOptions options, Field field) { - Contract.Requires(field != null); - using (var wr = new StringWriter()) { - var pr = new Printer(wr, options); - pr.PrintField(field, 0); - return ToStringWithoutNewline(wr); - } - } - - public static string FunctionSignatureToString(DafnyOptions options, Function f) { - Contract.Requires(f != null); - using var wr = new StringWriter(); - var pr = new Printer(wr, options); - pr.PrintFunction(f, 0, true); - return ToStringWithoutNewline(wr); - } - - public static string MethodSignatureToString(DafnyOptions options, Method m) { - Contract.Requires(m != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr, options); - pr.PrintMethod(m, 0, true); - return ToStringWithoutNewline(wr); - } - } - - /// - /// Returns a string for all attributes on the list "a". Each attribute is - /// followed by a space. - /// - public static string AttributesToString(DafnyOptions options, Attributes a) { - if (a == null) { - return ""; - } else { - return AttributesToString(options, a.Prev) + OneAttributeToString(options, a) + " "; - } - } - - public static string OneAttributeToString(DafnyOptions options, Attributes a, string nameSubstitution = null) { - Contract.Requires(a != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr, options); - pr.PrintOneAttribute(a, nameSubstitution); - return ToStringWithoutNewline(wr); - } - } - - public static string ToStringWithoutNewline(System.IO.StringWriter wr) { - Contract.Requires(wr != null); - var sb = wr.GetStringBuilder(); - var len = sb.Length; - while (len > 0 && (sb[len - 1] == '\n' || sb[len - 1] == '\r')) { - len--; - } - return sb.ToString(0, len); - } - - public void PrintProgramLargeStack(Program prog, bool afterResolver) { - DafnyMain.LargeStackFactory.StartNew(() => PrintProgram(prog, afterResolver)).Wait(); - } - - public void PrintProgram(Program prog, bool afterResolver) { - Contract.Requires(prog != null); - this.afterResolver = afterResolver; - if (options.ShowEnv != Bpl.ExecutionEngineOptions.ShowEnvironment.Never) { - wr.WriteLine("// " + options.Version); - wr.WriteLine("// " + options.Environment); - } - if (options.PrintMode != PrintModes.Serialization) { - wr.WriteLine("// {0}", prog.Name); - } - if (options.DafnyPrintResolvedFile != null && options.PrintMode == PrintModes.Everything) { - wr.WriteLine(); - wr.WriteLine("/*"); - PrintModuleDefinition(prog.Compilation, prog.SystemModuleManager.SystemModule, null, 0, null, Path.GetFullPath(options.DafnyPrintResolvedFile)); - wr.Write("// bitvector types in use:"); - foreach (var w in prog.SystemModuleManager.Bitwidths) { - wr.Write(" bv{0}", w); - } - wr.WriteLine(); - wr.WriteLine("*/"); - } - wr.WriteLine(); - PrintCallGraph(prog.DefaultModuleDef, 0); - PrintTopLevelDecls(prog.Compilation, prog.DefaultModuleDef.TopLevelDecls, 0, null, Path.GetFullPath(prog.FullName)); - foreach (var tup in prog.DefaultModuleDef.PrefixNamedModules) { - var decls = new List() { tup.Module }; - PrintTopLevelDecls(prog.Compilation, decls, 0, tup.Parts, Path.GetFullPath(prog.FullName)); - } - wr.Flush(); - } - - public void PrintCallGraph(ModuleDefinition module, int indent) { - Contract.Requires(module != null); - Contract.Requires(0 <= indent); - if (options.DafnyPrintResolvedFile != null && options.PrintMode == PrintModes.Everything) { - // print call graph - Indent(indent); wr.WriteLine("/* CALL GRAPH for module {0}:", module.Name); - var SCCs = module.CallGraph.TopologicallySortedComponents(); - // Sort output SCCs in order of: descending height, then decreasing size of SCC, then alphabetical order of the name of - // the representative element. By being this specific, we reduce changes in output from minor changes in the code. (With - // more effort, we could be even more deterministic, if needed in the future.) - SCCs.Sort((m, n) => { - var mm = module.CallGraph.GetSCCRepresentativePredecessorCount(m); - var nn = module.CallGraph.GetSCCRepresentativePredecessorCount(n); - if (mm < nn) { - return 1; - } else if (mm > nn) { - return -1; - } - mm = module.CallGraph.GetSCCSize(m); - nn = module.CallGraph.GetSCCSize(n); - if (mm < nn) { - return 1; - } else if (mm > nn) { - return -1; - } - return string.CompareOrdinal(m.NameRelativeToModule, n.NameRelativeToModule); - }); - foreach (var callable in SCCs) { - Indent(indent); - wr.WriteLine(" * SCC at height {0}:", module.CallGraph.GetSCCRepresentativePredecessorCount(callable)); - var r = module.CallGraph.GetSCC(callable); - foreach (var m in r) { - Indent(indent); - var maybeByMethod = m is Method method && method.IsByMethod ? " (by method)" : ""; - wr.WriteLine($" * {m.NameRelativeToModule}{maybeByMethod}"); - } - } - Indent(indent); wr.WriteLine(" */"); - } - } - - public void PrintTopLevelDecls(CompilationData compilation, IEnumerable decls, int indent, IEnumerable/*?*/ prefixIds, string fileBeingPrinted) { - Contract.Requires(decls != null); - int i = 0; - foreach (TopLevelDecl d in decls) { - Contract.Assert(d != null); - if (PrintModeSkipGeneral(d.tok, fileBeingPrinted)) { continue; } - if (d is AbstractTypeDecl) { - var at = (AbstractTypeDecl)d; - if (i++ != 0) { wr.WriteLine(); } - Indent(indent); - PrintClassMethodHelper("type", at.Attributes, at.Name + TPCharacteristicsSuffix(at.Characteristics), d.TypeArgs); - PrintExtendsClause(at); - if (at.Members.Count == 0) { - wr.WriteLine(); - } else { - wr.WriteLine(" {"); - PrintMembers(at.Members, indent + IndentAmount, fileBeingPrinted); - Indent(indent); - wr.WriteLine("}"); - } - } else if (d is NewtypeDecl) { - var dd = (NewtypeDecl)d; - if (i++ != 0) { wr.WriteLine(); } - Indent(indent); - PrintClassMethodHelper("newtype", dd.Attributes, dd.Name, new List()); - PrintExtendsClause(dd); - wr.Write(" = "); - if (dd.Var == null) { - PrintType(dd.BaseType); - wr.WriteLine(); - } else { - wr.Write(dd.Var.DisplayName); - if (ShowType(dd.Var.Type)) { - wr.Write(": "); - PrintType(dd.BaseType); - } - wr.WriteLine(); - Indent(indent + IndentAmount); - wr.Write("| "); - PrintExpression(dd.Constraint, true); - wr.WriteLine(); - if (dd.WitnessKind != SubsetTypeDecl.WKind.CompiledZero) { - Indent(indent + IndentAmount); - PrintWitnessClause(dd); - wr.WriteLine(); - } - } - if (dd.Members.Count != 0) { - Indent(indent); - wr.WriteLine("{"); - PrintMembers(dd.Members, indent + IndentAmount, fileBeingPrinted); - Indent(indent); - wr.WriteLine("}"); - } - } else if (d is SubsetTypeDecl subsetTypeDecl) { - if (i++ != 0) { wr.WriteLine(); } - - PrintSubsetTypeDecl(subsetTypeDecl, indent); - } else if (d is TypeSynonymDecl) { - var dd = (TypeSynonymDecl)d; - if (i++ != 0) { wr.WriteLine(); } - Indent(indent); - PrintClassMethodHelper("type", dd.Attributes, dd.Name + TPCharacteristicsSuffix(dd.Characteristics), dd.TypeArgs); - wr.Write(" = "); - PrintType(dd.Rhs); - wr.WriteLine(); - } else if (d is DatatypeDecl) { - var dd = (DatatypeDecl)d; - if (i++ != 0) { wr.WriteLine(); } - PrintDatatype(dd, indent, fileBeingPrinted); - } else if (d is IteratorDecl) { - var iter = (IteratorDecl)d; - if (i++ != 0) { wr.WriteLine(); } - PrintIteratorSignature(iter, indent); - - if (iter.Body != null) { - Indent(indent); - PrintStatement(iter.Body, indent); - wr.WriteLine(); - } - - if (afterResolver) { - // also print the members that were created as part of the interpretation of the iterator - Contract.Assert(iter.Members.Count != 0); // filled in during resolution - Indent(indent); wr.WriteLine("/*---------- iterator members ----------"); - Indent(indent); PrintIteratorClass(iter, indent, fileBeingPrinted); - Indent(indent); wr.WriteLine("---------- iterator members ----------*/"); - } - - } else if (d is DefaultClassDecl defaultClassDecl) { - if (defaultClassDecl.Members.Count == 0) { - // print nothing - } else { - if (i++ != 0) { wr.WriteLine(); } - PrintMembers(defaultClassDecl.Members, indent, fileBeingPrinted); - } - } else if (d is ClassLikeDecl) { - var cl = (ClassLikeDecl)d; - if (i++ != 0) { wr.WriteLine(); } - PrintClass(cl, indent, fileBeingPrinted); - - } else if (d is ClassLikeDecl) { - var cl = (ClassLikeDecl)d; - if (i++ != 0) { wr.WriteLine(); } - PrintClass(cl, indent, fileBeingPrinted); - - } else if (d is ValuetypeDecl) { - var vtd = (ValuetypeDecl)d; - if (i++ != 0) { wr.WriteLine(); } - Indent(indent); - PrintClassMethodHelper("type", vtd.Attributes, vtd.Name, vtd.TypeArgs); - if (vtd.Members.Count == 0) { - wr.WriteLine(" { }"); - } else { - wr.WriteLine(" {"); - PrintMembers(vtd.Members, indent + IndentAmount, fileBeingPrinted); - Indent(indent); - wr.WriteLine("}"); - } - - } else if (d is ModuleDecl md) { - wr.WriteLine(); - Indent(indent); - if (d is LiteralModuleDecl modDecl) { - if (printMode == PrintModes.Serialization && !modDecl.ModuleDef.ShouldCompile(compilation)) { - // This mode is used to losslessly serialize the source program by the C# and Library backends. - // Backends don't compile any code for modules not marked for compilation, - // so it's consistent to skip those modules here too. - continue; - } - - VisibilityScope scope = null; - if (modDecl.Signature != null) { - scope = modDecl.Signature.VisibilityScope; - } - PrintModuleDefinition(compilation, modDecl.ModuleDef, scope, indent, prefixIds, fileBeingPrinted); - } else if (d is AliasModuleDecl) { - var dd = (AliasModuleDecl)d; - - wr.Write("import"); - if (dd.Opened) { - wr.Write(" opened"); - } - wr.Write(" {0}", dd.Name); - if (dd.Name != dd.TargetQId.ToString()) { - wr.Write(" = {0}", dd.TargetQId.ToString()); - } - if (dd.Exports.Count == 1) { - wr.Write("`{0}", dd.Exports[0].val); - } - if (dd.Exports.Count > 1) { - wr.Write("`{{{0}}}", Util.Comma(dd.Exports, id => id.val)); - } - wr.WriteLine(); - } else if (d is AbstractModuleDecl) { - var dd = (AbstractModuleDecl)d; - - wr.Write("import"); - if (dd.Opened) { - wr.Write(" opened"); - } - wr.Write(" {0} ", dd.Name); - wr.Write(": {0}", dd.QId.ToString()); - if (dd.Exports.Count > 0) { - wr.Write("`{{{0}}}", Util.Comma(dd.Exports, id => id.val)); - } - wr.WriteLine(); - - } else if (d is ModuleExportDecl) { - ModuleExportDecl e = (ModuleExportDecl)d; - if (!e.IsDefault) { - wr.Write("export {0}", e.Name); - } else { - wr.Write("export"); - } - - if (e.IsRefining) { - wr.Write(" ..."); - } - if (e.Extends.Count > 0) { - wr.Write(" extends {0}", Util.Comma(e.Extends, id => id.val)); - } - - wr.WriteLine(); - PrintModuleExportDecl(compilation, e, indent + IndentAmount, fileBeingPrinted); - wr.WriteLine(); - } else { - Contract.Assert(false); // unexpected ModuleDecl - } - } else { - Contract.Assert(false); // unexpected TopLevelDecl - } - } - } - - private void PrintSubsetTypeDecl(SubsetTypeDecl dd, int indent) { - Indent(indent); - PrintClassMethodHelper("type", dd.Attributes, dd.Name + TPCharacteristicsSuffix(dd.Characteristics), dd.TypeArgs); - wr.Write(" = "); - wr.Write(dd.Var.DisplayName); - if (ShowType(dd.Var.Type)) { - wr.Write(": "); - PrintType(dd.Rhs); - } - - if (dd is NonNullTypeDecl) { - wr.Write(" "); - } else { - wr.WriteLine(); - Indent(indent + IndentAmount); - } - - wr.Write("| "); - PrintExpression(dd.Constraint, true); - if (dd.WitnessKind != SubsetTypeDecl.WKind.CompiledZero) { - if (dd is NonNullTypeDecl) { - wr.Write(" "); - } else { - wr.WriteLine(); - Indent(indent + IndentAmount); - } - - PrintWitnessClause(dd); - } - - wr.WriteLine(); - } - - private void PrintWitnessClause(RedirectingTypeDecl dd) { - Contract.Requires(dd != null); - Contract.Requires(dd.WitnessKind != SubsetTypeDecl.WKind.CompiledZero); - - switch (dd.WitnessKind) { - case SubsetTypeDecl.WKind.Ghost: - wr.Write("ghost "); - goto case SubsetTypeDecl.WKind.Compiled; - case SubsetTypeDecl.WKind.Compiled: - wr.Write("witness "); - PrintExpression(dd.Witness, true); - break; - case SubsetTypeDecl.WKind.OptOut: - wr.Write("witness *"); - break; - case SubsetTypeDecl.WKind.Special: - wr.Write("/*special witness*/"); - break; - case SubsetTypeDecl.WKind.CompiledZero: - default: - Contract.Assert(false); // unexpected WKind - break; - } - } - - void PrintModuleExportDecl(CompilationData compilation, ModuleExportDecl m, int indent, string fileBeingPrinted) { - Contract.Requires(m != null); - - if (m.RevealAll) { - Indent(indent); - wr.WriteLine("reveals *"); - } - if (m.ProvideAll) { - Indent(indent); - wr.WriteLine("provides *"); - } - var i = 0; - while (i < m.Exports.Count) { - var start = i; - var bodyKind = m.Exports[start].Opaque; - do { - i++; - } while (i < m.Exports.Count && m.Exports[i].Opaque == bodyKind); - // print [start..i) - Indent(indent); - wr.Write("{0} ", bodyKind ? "provides" : "reveals"); - wr.WriteLine(Util.Comma(i - start, j => m.Exports[start + j].ToString())); - - if (options.DafnyPrintResolvedFile != null) { - Contract.Assert(!printingExportSet); - printingExportSet = true; - Indent(indent); - wr.WriteLine("/*----- exported view:"); - for (int j = start; j < i; j++) { - var id = m.Exports[j]; - if (id.Decl is TopLevelDecl) { - PrintTopLevelDecls(compilation, new List { (TopLevelDecl)id.Decl }, indent + IndentAmount, null, fileBeingPrinted); - } else if (id.Decl is MemberDecl) { - PrintMembers(new List { (MemberDecl)id.Decl }, indent + IndentAmount, fileBeingPrinted); - } - } - Indent(indent); - wr.WriteLine("-----*/"); - Contract.Assert(printingExportSet); - printingExportSet = false; - } - } - } - - public void PrintModuleDefinition(CompilationData compilation, ModuleDefinition module, VisibilityScope scope, int indent, IEnumerable/*?*/ prefixIds, string fileBeingPrinted) { - Contract.Requires(module != null); - Contract.Requires(0 <= indent); - Type.PushScope(scope); - if (module.IsAbstract) { - wr.Write("abstract "); - } - wr.Write("module"); - PrintAttributes(module.Attributes); - wr.Write(" "); - if (prefixIds != null) { - foreach (var p in prefixIds) { - wr.Write("{0}.", p.val); - } - } - wr.Write("{0} ", module.Name); - if (module.RefinementQId != null) { - wr.Write("refines {0} ", module.RefinementQId); - } - if (!module.TopLevelDecls.Any()) { - wr.WriteLine("{ }"); - } else { - wr.WriteLine("{"); - PrintCallGraph(module, indent + IndentAmount); - PrintTopLevelDeclsOrExportedView(compilation, module, indent, fileBeingPrinted); - Indent(indent); - wr.WriteLine("}"); - } - Type.PopScope(scope); - } - - void PrintTopLevelDeclsOrExportedView(CompilationData compilation, ModuleDefinition module, int indent, string fileBeingPrinted) { - var decls = module.TopLevelDecls; - // only filter based on view name after resolver. - if (afterResolver && options.DafnyPrintExportedViews.Count != 0) { - var views = options.DafnyPrintExportedViews.ToHashSet(); - decls = decls.Where(d => views.Contains(d.FullName)); - } - PrintTopLevelDecls(compilation, decls, indent + IndentAmount, null, fileBeingPrinted); - foreach (var tup in module.PrefixNamedModules) { - PrintTopLevelDecls(compilation, new TopLevelDecl[] { tup.Module }, indent + IndentAmount, tup.Parts, fileBeingPrinted); - } - } - - void PrintIteratorSignature(IteratorDecl iter, int indent) { - Indent(indent); - PrintClassMethodHelper("iterator", iter.Attributes, iter.Name, iter.TypeArgs); - if (iter.IsRefining) { - wr.Write(" ..."); - } else { - PrintFormals(iter.Ins, iter); - if (iter.Outs.Count != 0) { - if (iter.Ins.Count + iter.Outs.Count <= 3) { - wr.Write(" yields "); - } else { - wr.WriteLine(); - Indent(indent + 2 * IndentAmount); - wr.Write("yields "); - } - PrintFormals(iter.Outs, iter); - } - } - - int ind = indent + IndentAmount; - PrintSpec("requires", iter.Requires, ind); - if (iter.Reads.Expressions != null) { - PrintFrameSpecLine("reads", iter.Reads.Expressions, ind, iter.Reads.HasAttributes() ? iter.Reads.Attributes : null); - } - if (iter.Modifies.Expressions != null) { - PrintFrameSpecLine("modifies", iter.Modifies.Expressions, ind, iter.Modifies.HasAttributes() ? iter.Modifies.Attributes : null); - } - PrintSpec("yield requires", iter.YieldRequires, ind); - PrintSpec("yield ensures", iter.YieldEnsures, ind); - PrintSpec("ensures", iter.Ensures, ind); - PrintDecreasesSpec(iter.Decreases, ind); - wr.WriteLine(); - } - - private void PrintIteratorClass(IteratorDecl iter, int indent, string fileBeingPrinted) { - PrintClassMethodHelper("class", null, iter.Name, iter.TypeArgs); - wr.WriteLine(" {"); - PrintMembers(iter.Members, indent + IndentAmount, fileBeingPrinted); - Indent(indent); wr.WriteLine("}"); - - Contract.Assert(iter.NonNullTypeDecl != null); - PrintSubsetTypeDecl(iter.NonNullTypeDecl, indent); - } - - public void PrintClass(ClassLikeDecl c, int indent, string fileBeingPrinted) { - Contract.Requires(c != null); - - Indent(indent); - PrintClassMethodHelper((c is TraitDecl) ? "trait" : "class", c.Attributes, c.Name, c.TypeArgs); - if (c.IsRefining) { - wr.Write(" ..."); - } else { - PrintExtendsClause(c); - } - - if (c.Members.Count == 0) { - wr.WriteLine(" { }"); - } else { - wr.WriteLine(" {"); - PrintMembers(c.Members, indent + IndentAmount, fileBeingPrinted); - Indent(indent); - wr.WriteLine("}"); - } - - if (options.DafnyPrintResolvedFile != null && c.NonNullTypeDecl != null) { - if (!printingExportSet) { - Indent(indent); wr.WriteLine("/*-- non-null type"); - } - PrintSubsetTypeDecl(c.NonNullTypeDecl, indent); - if (!printingExportSet) { - Indent(indent); wr.WriteLine("*/"); - } - } - } - - private void PrintExtendsClause(TopLevelDeclWithMembers c) { - string sep = " extends "; - foreach (var trait in c.ParentTraits) { - wr.Write(sep); - PrintType(trait); - sep = ", "; - } - } - - public void PrintMembers(List members, int indent, string fileBeingPrinted) { - Contract.Requires(members != null); - - int state = 0; // 0 - no members yet; 1 - previous member was a field; 2 - previous member was non-field - foreach (MemberDecl m in members) { - if (PrintModeSkipGeneral(m.tok, fileBeingPrinted)) { continue; } - if (printMode == PrintModes.Serialization && Attributes.Contains(m.Attributes, "auto_generated")) { - // omit this declaration - } else if (m is Method) { - if (state != 0) { wr.WriteLine(); } - PrintMethod((Method)m, indent, false); - var com = m as ExtremeLemma; - if (com != null && com.PrefixLemma != null) { - Indent(indent); wr.WriteLine("/***"); - PrintMethod(com.PrefixLemma, indent, false); - Indent(indent); wr.WriteLine("***/"); - } - state = 2; - } else if (m is Field) { - if (state == 2) { wr.WriteLine(); } - PrintField((Field)m, indent); - state = 1; - } else if (m is Function) { - if (state != 0) { wr.WriteLine(); } - PrintFunction((Function)m, indent, false); - if (m is ExtremePredicate fixp && fixp.PrefixPredicate != null) { - Indent(indent); wr.WriteLine("/*** (note, what is printed here does not show substitutions of calls to prefix predicates)"); - PrintFunction(fixp.PrefixPredicate, indent, false); - Indent(indent); wr.WriteLine("***/"); - } - state = 2; - } else { - Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member - } - } - } - - /// - /// Prints no space before "kind", but does print a space before "attrs" and "name". - /// - void PrintClassMethodHelper(string kind, Attributes attrs, string name, List typeArgs) { - Contract.Requires(kind != null); - Contract.Requires(name != null); - Contract.Requires(typeArgs != null); - - wr.Write(kind); - PrintAttributes(attrs); - - if (ArrowType.IsArrowTypeName(name)) { - PrintArrowType(ArrowType.ANY_ARROW, name, typeArgs); - } else if (ArrowType.IsPartialArrowTypeName(name)) { - PrintArrowType(ArrowType.PARTIAL_ARROW, name, typeArgs); - } else if (ArrowType.IsTotalArrowTypeName(name)) { - PrintArrowType(ArrowType.TOTAL_ARROW, name, typeArgs); - } else if (SystemModuleManager.IsTupleTypeName(name)) { - wr.Write(" /*{0}*/ ({1})", name, Util.Comma(typeArgs, TypeParamString)); - } else { - wr.Write(" {0}", name); - PrintTypeParams(typeArgs); - } - } - - private void PrintTypeParams(List typeArgs) { - Contract.Requires(typeArgs != null); - Contract.Requires( - typeArgs.All(tp => tp.Name.StartsWith("_")) || - typeArgs.All(tp => !tp.Name.StartsWith("_"))); - - if (typeArgs.Count != 0 && !typeArgs[0].Name.StartsWith("_")) { - wr.Write("<{0}>", Util.Comma(typeArgs, TypeParamString)); - } - } - - public string TypeParamString(TypeParameter tp) { - Contract.Requires(tp != null); - string variance; - switch (tp.VarianceSyntax) { - case TypeParameter.TPVarianceSyntax.Covariant_Permissive: - variance = "*"; - break; - case TypeParameter.TPVarianceSyntax.Covariant_Strict: - variance = "+"; - break; - case TypeParameter.TPVarianceSyntax.NonVariant_Permissive: - variance = "!"; - break; - case TypeParameter.TPVarianceSyntax.NonVariant_Strict: - variance = ""; - break; - case TypeParameter.TPVarianceSyntax.Contravariance: - variance = "-"; - break; - default: - Contract.Assert(false); // unexpected VarianceSyntax - throw new cce.UnreachableException(); - } - return variance + tp.Name + TPCharacteristicsSuffix(tp.Characteristics); - } - - private void PrintArrowType(string arrow, string internalName, List typeArgs) { - Contract.Requires(arrow != null); - Contract.Requires(internalName != null); - Contract.Requires(typeArgs != null); - Contract.Requires(1 <= typeArgs.Count); // argument list ends with the result type - wr.Write(" /*{0}*/ ", internalName); - int arity = typeArgs.Count - 1; - if (arity != 1) { - wr.Write("("); - } - wr.Write(Util.Comma(arity, i => TypeParamString(typeArgs[i]))); - if (arity != 1) { - wr.Write(")"); - } - wr.Write(" {0} {1}", arrow, TypeParamString(typeArgs[arity])); - } - - private void PrintTypeInstantiation(List typeArgs) { - Contract.Requires(typeArgs == null || typeArgs.Count != 0); - wr.Write(Type.TypeArgsToString(options, typeArgs)); - } - - public void PrintDatatype(DatatypeDecl dt, int indent, string fileBeingPrinted) { - Contract.Requires(dt != null); - Indent(indent); - PrintClassMethodHelper(dt is IndDatatypeDecl ? "datatype" : "codatatype", dt.Attributes, dt.Name, dt.TypeArgs); - PrintExtendsClause(dt); - wr.Write(" ="); - string sep = ""; - foreach (DatatypeCtor ctor in dt.Ctors) { - wr.Write(sep); - PrintClassMethodHelper(ctor.IsGhost ? " ghost" : "", ctor.Attributes, ctor.Name, new List()); - if (ctor.Formals.Count != 0) { - PrintFormals(ctor.Formals, null); - } - sep = " |"; - } - if (dt.Members.Count == 0) { - wr.WriteLine(); - } else { - wr.WriteLine(" {"); - PrintMembers(dt.Members, indent + IndentAmount, fileBeingPrinted); - Indent(indent); - wr.WriteLine("}"); - } - } - - /// - /// Prints a space before each attribute. - /// - public void PrintAttributes(Attributes a) { - if (a != null) { - PrintAttributes(a.Prev); - wr.Write(" "); - PrintOneAttribute(a); - } - } - public void PrintOneAttribute(Attributes a, string nameSubstitution = null) { - Contract.Requires(a != null); - var name = nameSubstitution ?? a.Name; - var usAttribute = name.StartsWith("_") || (options.DisallowExterns && name == "extern"); - wr.Write("{1}{{:{0}", name, usAttribute ? "/*" : ""); - if (a.Args != null) { - PrintAttributeArgs(a.Args, false); - } - wr.Write("}}{0}", usAttribute ? "*/" : ""); - - } - - public void PrintAttributeArgs(List args, bool isFollowedBySemicolon) { - Contract.Requires(args != null); - string prefix = " "; - foreach (var arg in args) { - Contract.Assert(arg != null); - wr.Write(prefix); - prefix = ", "; - PrintExpression(arg, isFollowedBySemicolon); - } - } - - public void PrintField(Field field, int indent) { - Contract.Requires(field != null); - Indent(indent); - if (field.HasStaticKeyword) { - wr.Write("static "); - } - if (field.IsGhost) { - wr.Write("ghost "); - } - if (field is ConstantField) { - wr.Write("const"); - } else { - wr.Write("var"); - } - PrintAttributes(field.Attributes); - wr.Write(" {0}", field.Name); - if (ShowType(field.Type)) { - wr.Write(": "); - PrintType(field.Type); - } - if (field is ConstantField) { - var c = (ConstantField)field; - if (c.Rhs != null) { - wr.Write(" := "); - PrintExpression(c.Rhs, true); - } - } else if (field.IsUserMutable) { - // nothing more to say - } else if (field.IsMutable) { - wr.Write(" // non-assignable"); - } else { - wr.Write(" // immutable"); - } - wr.WriteLine(); - } - - public void PrintFunction(Function f, int indent, bool printSignatureOnly) { - Contract.Requires(f != null); - - if (PrintModeSkipFunctionOrMethod(f.IsGhost, f.Attributes, f.Name)) { return; } - Indent(indent); - PrintClassMethodHelper(f.GetFunctionDeclarationKeywords(options), f.Attributes, f.Name, f.TypeArgs); - if (f.SignatureIsOmitted) { - wr.Write(" ..."); - } else { - if (f is ExtremePredicate) { - PrintKTypeIndication(((ExtremePredicate)f).TypeOfK); - } - PrintFormals(f.Formals, f, f.Name); - if (f.Result != null || (f is not Predicate && f is not ExtremePredicate && f is not TwoStatePredicate && f is not PrefixPredicate)) { - wr.Write(": "); - if (f.Result != null) { - wr.Write("("); - PrintFormal(f.Result, false); - wr.Write(")"); - } else { - PrintType(f.ResultType); - } - } - } - - int ind = indent + IndentAmount; - PrintSpec("requires", f.Req, ind); - PrintFrameSpecLine("reads", f.Reads, ind, null); - PrintSpec("ensures", f.Ens, ind); - PrintDecreasesSpec(f.Decreases, ind); - wr.WriteLine(); - if (f.Body != null && !printSignatureOnly) { - Indent(indent); - wr.WriteLine("{"); - PrintExtendedExpr(f.Body, ind, true, false); - Indent(indent); - wr.Write("}"); - if (f.ByMethodBody != null) { - wr.Write(" by method "); - if (options.DafnyPrintResolvedFile != null && f.ByMethodDecl != null) { - Contract.Assert(f.ByMethodDecl.Ens.Count == 1); - wr.Write("/* ensures"); - PrintAttributedExpression(f.ByMethodDecl.Ens[0]); - wr.Write(" */ "); - } - PrintStatement(f.ByMethodBody, indent); - } - wr.WriteLine(); - } - } - - // ----------------------------- PrintMethod ----------------------------- - - const int IndentAmount = 2; // The amount of indent for each new scope - void Indent(int amount) { - Contract.Requires(0 <= amount); - wr.Write(new String(' ', amount)); - } - - private bool PrintModeSkipFunctionOrMethod(bool IsGhost, Attributes attributes, string name) { - if (printMode == PrintModes.NoGhost && IsGhost) { return true; } - if (printMode == PrintModes.NoIncludes || printMode == PrintModes.NoGhost) { - bool verify = true; - if (Attributes.ContainsBool(attributes, "verify", ref verify) && !verify) { return true; } - if (name.Contains("INTERNAL") || name.StartsWith("reveal_")) { return true; } - } - return false; - } - - private bool PrintModeSkipGeneral(IToken tok, string fileBeingPrinted) { - return (printMode == PrintModes.NoIncludes || printMode == PrintModes.NoGhost) - && tok.Uri != null && fileBeingPrinted != null && tok.Uri.LocalPath != fileBeingPrinted; - } - - public void PrintMethod(Method method, int indent, bool printSignatureOnly) { - Contract.Requires(method != null); - - if (PrintModeSkipFunctionOrMethod(method.IsGhost, method.Attributes, method.Name)) { return; } - Indent(indent); - string k = method is Constructor ? "constructor" : - method is LeastLemma ? "least lemma" : - method is GreatestLemma ? "greatest lemma" : - method is Lemma || method is PrefixLemma ? "lemma" : - method is TwoStateLemma ? "twostate lemma" : - "method"; - if (method.HasStaticKeyword) { k = "static " + k; } - if (method.IsGhost && !method.IsLemmaLike) { - k = "ghost " + k; - } - string nm = method is Constructor && !((Constructor)method).HasName ? "" : method.Name; - PrintClassMethodHelper(k, method.Attributes, nm, method.TypeArgs); - if (method.SignatureIsOmitted) { - wr.Write(" ..."); - } else { - if (method is ExtremeLemma) { - PrintKTypeIndication(((ExtremeLemma)method).TypeOfK); - } - PrintFormals(method.Ins, method, method.Name); - if (method.Outs.Count != 0) { - if (method.Ins.Count + method.Outs.Count <= 3) { - wr.Write(" returns "); - } else { - wr.WriteLine(); - Indent(indent + 2 * IndentAmount); - wr.Write("returns "); - } - PrintFormals(method.Outs, method); - } - } - - int ind = indent + IndentAmount; - PrintSpec("requires", method.Req, ind); - if (method.Mod.Expressions != null) { - PrintFrameSpecLine("modifies", method.Mod.Expressions, ind, method.Mod.HasAttributes() ? method.Mod.Attributes : null); - } - PrintSpec("ensures", method.Ens, ind); - PrintDecreasesSpec(method.Decreases, ind); - wr.WriteLine(); - - if (method.Body != null && !printSignatureOnly) { - Indent(indent); - PrintStatement(method.Body, indent); - wr.WriteLine(); - } - } - - void PrintKTypeIndication(ExtremePredicate.KType kType) { - switch (kType) { - case ExtremePredicate.KType.Nat: - wr.Write("[nat]"); - break; - case ExtremePredicate.KType.ORDINAL: - wr.Write("[ORDINAL]"); - break; - case ExtremePredicate.KType.Unspecified: - break; - default: - Contract.Assume(false); // unexpected KType value - break; - } - } - - internal void PrintFormals(List ff, ICallable/*?*/ context, string name = null) { - Contract.Requires(ff != null); - if (name != null && name.EndsWith("#")) { - wr.Write("["); - PrintFormal(ff[0], false); - wr.Write("]"); - ff = new List(ff.Skip(1)); - } - wr.Write("("); - string sep = ""; - foreach (Formal f in ff) { - Contract.Assert(f != null); - wr.Write(sep); - sep = ", "; - PrintFormal(f, (context is TwoStateLemma || context is TwoStateFunction) && f.InParam); - } - wr.Write(")"); - } - - void PrintFormal(Formal f, bool showNewKeyword) { - Contract.Requires(f != null); - if (showNewKeyword && !f.IsOld) { - wr.Write("new "); - } - if (f.IsOlder) { - Contract.Assert(f.HasName); - wr.Write("older "); - } - if (f.IsGhost) { - wr.Write("ghost "); - } - if (f.IsNameOnly) { - Contract.Assert(f.HasName); - wr.Write("nameonly "); - } - if (f.HasName) { - wr.Write("{0}: ", f.DisplayName); - } - PrintType(f.Type); - if (f.DefaultValue != null) { - wr.Write(" := "); - PrintExpression(f.DefaultValue, false); - } - } - - internal void PrintDecreasesSpec(Specification decs, int indent) { - Contract.Requires(decs != null); - if (printMode == PrintModes.NoGhost) { return; } - if (decs.Expressions != null && decs.Expressions.Count != 0) { - wr.WriteLine(); - Indent(indent); - wr.Write("decreases"); - if (decs.HasAttributes()) { - PrintAttributes(decs.Attributes); - } - wr.Write(" "); - PrintExpressionList(decs.Expressions, true); - } - } - - internal void PrintFrameSpecLine(string kind, List ee, int indent, Attributes attrs) { - Contract.Requires(kind != null); - Contract.Requires(cce.NonNullElements(ee)); - if (ee != null && ee.Count != 0) { - wr.WriteLine(); - Indent(indent); - wr.Write("{0}", kind); - if (attrs != null) { - PrintAttributes(attrs); - } - wr.Write(" "); - PrintFrameExpressionList(ee); - } - } - - internal void PrintSpec(string kind, List ee, int indent) { - Contract.Requires(kind != null); - Contract.Requires(ee != null); - if (printMode == PrintModes.NoGhost) { return; } - foreach (AttributedExpression e in ee) { - Contract.Assert(e != null); - wr.WriteLine(); - Indent(indent); - wr.Write("{0}", kind); - PrintAttributedExpression(e); - } - } - - void PrintAttributedExpression(AttributedExpression e) { - Contract.Requires(e != null); - - if (e.HasAttributes()) { - PrintAttributes(e.Attributes); - } - - wr.Write(" "); - if (e.Label != null) { - wr.Write("{0}: ", e.Label.Name); - } - PrintExpression(e.E, true); - } - - // ----------------------------- PrintType ----------------------------- - - public void PrintType(Type ty) { - Contract.Requires(ty != null); - wr.Write(ty.TypeName(options, null, true)); - } - - public void PrintType(string prefix, Type ty) { - Contract.Requires(prefix != null); - Contract.Requires(ty != null); - if (options.DafnyPrintResolvedFile != null) { - ty = ty.Normalize(); - } - string s = ty.TypeName(options, null, true); - if (!(ty is TypeProxy) && !s.StartsWith("_")) { - wr.Write("{0}{1}", prefix, s); - } - } - - public string TPCharacteristicsSuffix(TypeParameter.TypeParameterCharacteristics characteristics) { - string s = null; - if (characteristics.EqualitySupport == TypeParameter.EqualitySupportValue.Required || - (characteristics.EqualitySupport == TypeParameter.EqualitySupportValue.InferredRequired && options.DafnyPrintResolvedFile != null)) { - s = "=="; - } - if (characteristics.HasCompiledValue) { - var prefix = s == null ? "" : s + ","; - s = prefix + "0"; - } else if (characteristics.IsNonempty) { - var prefix = s == null ? "" : s + ","; - s = prefix + "00"; - } - if (characteristics.ContainsNoReferenceTypes) { - var prefix = s == null ? "" : s + ","; - s = prefix + "!new"; - } - if (s == null) { - return ""; - } else { - return "(" + s + ")"; - } - } - - bool ShowType(Type t) { - Contract.Requires(t != null); - return !(t is TypeProxy) || options.DafnyPrintResolvedFile != null; - } - - // ----------------------------- PrintStatement ----------------------------- - - /// - /// Prints from the current position of the current line. - /// If the statement requires several lines, subsequent lines are indented at "indent". - /// No newline is printed after the statement. - /// - public void PrintStatement(Statement stmt, int indent) { - Contract.Requires(stmt != null); - - if (stmt.IsGhost && printMode == PrintModes.NoGhost) { return; } - for (LList